Merge "Transcoder: Preserve source track's bitrate by default."
diff --git a/apex/manifest_codec.json b/apex/manifest_codec.json
index 2320fd7..1f05d2e 100644
--- a/apex/manifest_codec.json
+++ b/apex/manifest_codec.json
@@ -1,4 +1,7 @@
{
"name": "com.android.media.swcodec",
- "version": 300000000
+ "version": 300000000,
+ "requireNativeLibs": [
+ ":sphal"
+ ]
}
diff --git a/drm/mediacas/plugins/clearkey/ClearKeyFetcher.cpp b/drm/mediacas/plugins/clearkey/ClearKeyFetcher.cpp
index cb69f91..466e571 100644
--- a/drm/mediacas/plugins/clearkey/ClearKeyFetcher.cpp
+++ b/drm/mediacas/plugins/clearkey/ClearKeyFetcher.cpp
@@ -62,8 +62,8 @@
}
ALOGV("descriptor_size=%zu", container.descriptor_size());
- // Sanity check to verify that the BroadcastEncryptor is sending a properly
- // formed EcmContainer. If it contains two Ecms, the ids should have different
+ // Validate that the BroadcastEncryptor is sending a properly formed
+ // EcmContainer. If it contains two Ecms, the ids should have different
// parity (one odd, one even). This does not necessarily affect decryption
// but indicates a problem with Ecm generation.
if (container.descriptor_size() == 2) {
diff --git a/media/codec2/components/cmds/codec2.cpp b/media/codec2/components/cmds/codec2.cpp
index d6025de..a17b04e 100644
--- a/media/codec2/components/cmds/codec2.cpp
+++ b/media/codec2/components/cmds/codec2.cpp
@@ -138,7 +138,7 @@
SimplePlayer::SimplePlayer()
: mListener(new Listener(this)),
- mProducerListener(new DummyProducerListener),
+ mProducerListener(new StubProducerListener),
mLinearPoolId(C2BlockPool::PLATFORM_START),
mComposerClient(new SurfaceComposerClient) {
CHECK_EQ(mComposerClient->initCheck(), (status_t)OK);
diff --git a/media/codec2/components/gav1/Android.bp b/media/codec2/components/gav1/Android.bp
index 5c4abb7..f374089 100644
--- a/media/codec2/components/gav1/Android.bp
+++ b/media/codec2/components/gav1/Android.bp
@@ -13,8 +13,4 @@
srcs: ["C2SoftGav1Dec.cpp"],
static_libs: ["libgav1"],
-
- include_dirs: [
- "external/libgav1/libgav1/",
- ],
}
diff --git a/media/codec2/components/gav1/C2SoftGav1Dec.cpp b/media/codec2/components/gav1/C2SoftGav1Dec.cpp
index ec5f549..22aa905 100644
--- a/media/codec2/components/gav1/C2SoftGav1Dec.cpp
+++ b/media/codec2/components/gav1/C2SoftGav1Dec.cpp
@@ -288,9 +288,7 @@
void C2SoftGav1Dec::onRelease() { destroyDecoder(); }
c2_status_t C2SoftGav1Dec::onFlush_sm() {
- Libgav1StatusCode status =
- mCodecCtx->EnqueueFrame(/*data=*/nullptr, /*size=*/0,
- /*user_private_data=*/0);
+ Libgav1StatusCode status = mCodecCtx->SignalEOS();
if (status != kLibgav1StatusOk) {
ALOGE("Failed to flush av1 decoder. status: %d.", status);
return C2_CORRUPTED;
@@ -299,7 +297,7 @@
// Dequeue frame (if any) that was enqueued previously.
const libgav1::DecoderBuffer *buffer;
status = mCodecCtx->DequeueFrame(&buffer);
- if (status != kLibgav1StatusOk) {
+ if (status != kLibgav1StatusOk && status != kLibgav1StatusNothingToDequeue) {
ALOGE("Failed to dequeue frame after flushing the av1 decoder. status: %d",
status);
return C2_CORRUPTED;
@@ -433,7 +431,8 @@
TIME_DIFF(mTimeEnd, mTimeStart, delay);
const Libgav1StatusCode status =
- mCodecCtx->EnqueueFrame(bitstream, inSize, frameIndex);
+ mCodecCtx->EnqueueFrame(bitstream, inSize, frameIndex,
+ /*buffer_private_data=*/nullptr);
GETTIME(&mTimeEnd, nullptr);
TIME_DIFF(mTimeStart, mTimeEnd, decodeTime);
@@ -448,9 +447,7 @@
}
} else {
- const Libgav1StatusCode status =
- mCodecCtx->EnqueueFrame(/*data=*/nullptr, /*size=*/0,
- /*user_private_data=*/0);
+ const Libgav1StatusCode status = mCodecCtx->SignalEOS();
if (status != kLibgav1StatusOk) {
ALOGE("Failed to flush av1 decoder. status: %d.", status);
work->result = C2_CORRUPTED;
@@ -607,13 +604,14 @@
const libgav1::DecoderBuffer *buffer;
const Libgav1StatusCode status = mCodecCtx->DequeueFrame(&buffer);
- if (status != kLibgav1StatusOk) {
+ if (status != kLibgav1StatusOk && status != kLibgav1StatusNothingToDequeue) {
ALOGE("av1 decoder DequeueFrame failed. status: %d.", status);
return false;
}
- // |buffer| can be NULL if status was equal to kLibgav1StatusOk. This is not
- // an error. This could mean one of two things:
+ // |buffer| can be NULL if status was equal to kLibgav1StatusOk or
+ // kLibgav1StatusNothingToDequeue. This is not an error. This could mean one
+ // of two things:
// - The EnqueueFrame() call was either a flush (called with nullptr).
// - The enqueued frame did not have any displayable frames.
if (!buffer) {
@@ -722,9 +720,7 @@
return C2_OMITTED;
}
- Libgav1StatusCode status =
- mCodecCtx->EnqueueFrame(/*data=*/nullptr, /*size=*/0,
- /*user_private_data=*/0);
+ const Libgav1StatusCode status = mCodecCtx->SignalEOS();
if (status != kLibgav1StatusOk) {
ALOGE("Failed to flush av1 decoder. status: %d.", status);
return C2_CORRUPTED;
diff --git a/media/codec2/components/gav1/C2SoftGav1Dec.h b/media/codec2/components/gav1/C2SoftGav1Dec.h
index a7c08bb..555adc9 100644
--- a/media/codec2/components/gav1/C2SoftGav1Dec.h
+++ b/media/codec2/components/gav1/C2SoftGav1Dec.h
@@ -18,8 +18,8 @@
#define ANDROID_C2_SOFT_GAV1_DEC_H_
#include <SimpleC2Component.h>
-#include "libgav1/src/decoder.h"
-#include "libgav1/src/decoder_settings.h"
+#include "libgav1/src/gav1/decoder.h"
+#include "libgav1/src/gav1/decoder_settings.h"
#define GETTIME(a, b) gettimeofday(a, b);
#define TIME_DIFF(start, end, diff) \
diff --git a/media/codec2/components/vorbis/C2SoftVorbisDec.cpp b/media/codec2/components/vorbis/C2SoftVorbisDec.cpp
index 15564d9..a8b5377 100644
--- a/media/codec2/components/vorbis/C2SoftVorbisDec.cpp
+++ b/media/codec2/components/vorbis/C2SoftVorbisDec.cpp
@@ -279,6 +279,8 @@
// skip 7 <type + "vorbis"> bytes
makeBitReader((const uint8_t *)data + 7, inSize - 7, &buf, &ref, &bits);
if (data[0] == 1) {
+ // release any memory that vorbis_info_init will blindly overwrite
+ vorbis_info_clear(mVi);
vorbis_info_init(mVi);
if (0 != _vorbis_unpack_info(mVi, &bits)) {
ALOGE("Encountered error while unpacking info");
@@ -323,6 +325,8 @@
work->result = C2_CORRUPTED;
return;
}
+ // release any memory that vorbis_dsp_init will blindly overwrite
+ vorbis_dsp_clear(mState);
if (0 != vorbis_dsp_init(mState, mVi)) {
ALOGE("Encountered error while dsp init");
mSignalledError = true;
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp
index 0626c8d..369087d 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.cpp
+++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp
@@ -1718,7 +1718,13 @@
}
}
- if (notifyClient && !buffer && !flags) {
+ bool drop = false;
+ if (worklet->output.flags & C2FrameData::FLAG_DROP_FRAME) {
+ ALOGV("[%s] onWorkDone: drop buffer but keep metadata", mName);
+ drop = true;
+ }
+
+ if (notifyClient && !buffer && !flags && !(drop && outputFormat)) {
ALOGV("[%s] onWorkDone: Not reporting output buffer (%lld)",
mName, work->input.ordinal.frameIndex.peekull());
notifyClient = false;
@@ -1745,7 +1751,7 @@
return false;
}
output->buffers->pushToStash(
- buffer,
+ drop ? nullptr : buffer,
notifyClient,
timestamp.peek(),
flags,
diff --git a/media/extractors/fuzzers/Android.bp b/media/extractors/fuzzers/Android.bp
index 594ee7b..e900e57 100644
--- a/media/extractors/fuzzers/Android.bp
+++ b/media/extractors/fuzzers/Android.bp
@@ -68,6 +68,7 @@
cc_defaults {
name: "mpeg2-extractor-fuzzer-defaults",
defaults: ["extractor-fuzzer-defaults"],
+ host_supported: true,
include_dirs: [
"frameworks/av/media/extractors/mpeg2",
@@ -80,6 +81,7 @@
"libstagefright_mpeg2extractor",
"libstagefright_esds",
"libmpeg2extractor",
+ "libmedia_helper",
],
shared_libs: [
@@ -329,4 +331,6 @@
],
dictionary: "midi_extractor_fuzzer.dict",
+
+ host_supported: true,
}
diff --git a/media/extractors/midi/Android.bp b/media/extractors/midi/Android.bp
index b8255fc..1c69bb8 100644
--- a/media/extractors/midi/Android.bp
+++ b/media/extractors/midi/Android.bp
@@ -5,7 +5,7 @@
srcs: ["MidiExtractor.cpp"],
header_libs: [
- "libmedia_headers",
+ "libmedia_datasource_headers",
],
static_libs: [
@@ -18,4 +18,12 @@
shared_libs: [
"libbase",
],
+
+ host_supported: true,
+
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
}
diff --git a/media/extractors/mp4/ItemTable.cpp b/media/extractors/mp4/ItemTable.cpp
index 0773387..2599c2c 100644
--- a/media/extractors/mp4/ItemTable.cpp
+++ b/media/extractors/mp4/ItemTable.cpp
@@ -546,11 +546,11 @@
continue;
}
ALOGV("Image item id %d uses thumbnail item id %d", mRefs[i], mItemId);
- ImageItem &masterImage = itemIdToItemMap.editValueAt(itemIndex);
- if (!masterImage.thumbnails.empty()) {
+ ImageItem &imageItem = itemIdToItemMap.editValueAt(itemIndex);
+ if (!imageItem.thumbnails.empty()) {
ALOGW("already has thumbnails!");
}
- masterImage.thumbnails.push_back(mItemId);
+ imageItem.thumbnails.push_back(mItemId);
}
break;
}
@@ -929,7 +929,7 @@
status_t IpcoBox::parse(off64_t offset, size_t size) {
ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
- // push dummy as the index is 1-based
+ // push a placeholder as the index is 1-based
mItemProperties->push_back(new ItemProperty());
return parseChunks(offset, size);
}
@@ -1614,17 +1614,17 @@
return BAD_VALUE;
}
- uint32_t masterItemIndex = mDisplayables[imageIndex];
+ uint32_t imageItemIndex = mDisplayables[imageIndex];
- const ImageItem &masterImage = mItemIdToItemMap[masterItemIndex];
- if (masterImage.thumbnails.empty()) {
- *itemIndex = masterItemIndex;
+ const ImageItem &imageItem = mItemIdToItemMap[imageItemIndex];
+ if (imageItem.thumbnails.empty()) {
+ *itemIndex = imageItemIndex;
return OK;
}
- ssize_t thumbItemIndex = mItemIdToItemMap.indexOfKey(masterImage.thumbnails[0]);
+ ssize_t thumbItemIndex = mItemIdToItemMap.indexOfKey(imageItem.thumbnails[0]);
if (thumbItemIndex < 0) {
- // Do not return the master image in this case, fail it so that the
+ // Do not return the image item in this case, fail it so that the
// thumbnail extraction code knows we really don't have it.
return INVALID_OPERATION;
}
diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp
index 019a99a..bd36403 100644
--- a/media/extractors/mp4/MPEG4Extractor.cpp
+++ b/media/extractors/mp4/MPEG4Extractor.cpp
@@ -1151,7 +1151,7 @@
} else if (chunk_type == FOURCC("moov")) {
mInitCheck = OK;
- return UNKNOWN_ERROR; // Return a dummy error.
+ return UNKNOWN_ERROR; // Return a generic error.
}
break;
}
@@ -5820,7 +5820,7 @@
return -EINVAL;
}
- // apply some sanity (vs strict legality) checks
+ // apply some quick (vs strict legality) checks
//
static constexpr uint32_t kMaxTrunSampleCount = 10000;
if (sampleCount > kMaxTrunSampleCount) {
diff --git a/media/libaaudio/examples/utils/dummy.cpp b/media/libaaudio/examples/utils/dummy.cpp
deleted file mode 100644
index 8ef7e36..0000000
--- a/media/libaaudio/examples/utils/dummy.cpp
+++ /dev/null
@@ -1,5 +0,0 @@
-/**
- * Dummy file needed to get Android Studio to scan this folder.
- */
-
-int g_DoNotUseThisVariable = 0;
diff --git a/media/libaaudio/examples/utils/unused.cpp b/media/libaaudio/examples/utils/unused.cpp
new file mode 100644
index 0000000..9a5205e
--- /dev/null
+++ b/media/libaaudio/examples/utils/unused.cpp
@@ -0,0 +1,5 @@
+/**
+ * Unused file required to get Android Studio to scan this folder.
+ */
+
+int g_DoNotUseThisVariable = 0;
diff --git a/media/libaaudio/src/binding/SharedMemoryParcelable.h b/media/libaaudio/src/binding/SharedMemoryParcelable.h
index 4ec38c5..3927f58 100644
--- a/media/libaaudio/src/binding/SharedMemoryParcelable.h
+++ b/media/libaaudio/src/binding/SharedMemoryParcelable.h
@@ -26,7 +26,7 @@
namespace aaudio {
-// Arbitrary limits for sanity checks. TODO remove after debugging.
+// Arbitrary limits for range checks.
#define MAX_SHARED_MEMORIES (32)
#define MAX_MMAP_OFFSET_BYTES (32 * 1024 * 8)
#define MAX_MMAP_SIZE_BYTES (32 * 1024 * 8)
diff --git a/media/libaaudio/src/flowgraph/AudioProcessorBase.h b/media/libaaudio/src/flowgraph/AudioProcessorBase.h
index eda46ae..972932f 100644
--- a/media/libaaudio/src/flowgraph/AudioProcessorBase.h
+++ b/media/libaaudio/src/flowgraph/AudioProcessorBase.h
@@ -267,7 +267,7 @@
AudioFloatInputPort input;
/**
- * Dummy processor. The work happens in the read() method.
+ * Do nothing. The work happens in the read() method.
*
* @param framePosition index of first frame to be processed
* @param numFrames
diff --git a/media/libaudioclient/AudioEffect.cpp b/media/libaudioclient/AudioEffect.cpp
index 3ead6cb..73b96ab 100644
--- a/media/libaudioclient/AudioEffect.cpp
+++ b/media/libaudioclient/AudioEffect.cpp
@@ -36,64 +36,10 @@
// ---------------------------------------------------------------------------
AudioEffect::AudioEffect(const String16& opPackageName)
- : mStatus(NO_INIT), mProbe(false), mOpPackageName(opPackageName)
+ : mOpPackageName(opPackageName)
{
}
-
-AudioEffect::AudioEffect(const effect_uuid_t *type,
- const String16& opPackageName,
- const effect_uuid_t *uuid,
- int32_t priority,
- effect_callback_t cbf,
- void* user,
- audio_session_t sessionId,
- audio_io_handle_t io,
- const AudioDeviceTypeAddr& device,
- bool probe
- )
- : mStatus(NO_INIT), mProbe(false), mOpPackageName(opPackageName)
-{
- AutoMutex lock(mConstructLock);
- mStatus = set(type, uuid, priority, cbf, user, sessionId, io, device, probe);
-}
-
-AudioEffect::AudioEffect(const char *typeStr,
- const String16& opPackageName,
- const char *uuidStr,
- int32_t priority,
- effect_callback_t cbf,
- void* user,
- audio_session_t sessionId,
- audio_io_handle_t io,
- const AudioDeviceTypeAddr& device,
- bool probe
- )
- : mStatus(NO_INIT), mProbe(false), mOpPackageName(opPackageName)
-{
- effect_uuid_t type;
- effect_uuid_t *pType = NULL;
- effect_uuid_t uuid;
- effect_uuid_t *pUuid = NULL;
-
- ALOGV("Constructor string\n - type: %s\n - uuid: %s", typeStr, uuidStr);
-
- if (typeStr != NULL) {
- if (stringToGuid(typeStr, &type) == NO_ERROR) {
- pType = &type;
- }
- }
-
- if (uuidStr != NULL) {
- if (stringToGuid(uuidStr, &uuid) == NO_ERROR) {
- pUuid = &uuid;
- }
- }
-
- AutoMutex lock(mConstructLock);
- mStatus = set(pType, pUuid, priority, cbf, user, sessionId, io, device, probe);
-}
-
status_t AudioEffect::set(const effect_uuid_t *type,
const effect_uuid_t *uuid,
int32_t priority,
@@ -194,6 +140,34 @@
return mStatus;
}
+status_t AudioEffect::set(const char *typeStr,
+ const char *uuidStr,
+ int32_t priority,
+ effect_callback_t cbf,
+ void* user,
+ audio_session_t sessionId,
+ audio_io_handle_t io,
+ const AudioDeviceTypeAddr& device,
+ bool probe)
+{
+ effect_uuid_t type;
+ effect_uuid_t *pType = nullptr;
+ effect_uuid_t uuid;
+ effect_uuid_t *pUuid = nullptr;
+
+ ALOGV("AudioEffect::set string\n - type: %s\n - uuid: %s",
+ typeStr ? typeStr : "nullptr", uuidStr ? uuidStr : "nullptr");
+
+ if (stringToGuid(typeStr, &type) == NO_ERROR) {
+ pType = &type;
+ }
+ if (stringToGuid(uuidStr, &uuid) == NO_ERROR) {
+ pUuid = &uuid;
+ }
+
+ return set(pType, pUuid, priority, cbf, user, sessionId, io, device, probe);
+}
+
AudioEffect::~AudioEffect()
{
diff --git a/media/libaudioclient/AudioRecord.cpp b/media/libaudioclient/AudioRecord.cpp
index df47def..509e063 100644
--- a/media/libaudioclient/AudioRecord.cpp
+++ b/media/libaudioclient/AudioRecord.cpp
@@ -1092,7 +1092,7 @@
}
if (ssize_t(userSize) < 0 || (buffer == NULL && userSize != 0)) {
- // sanity-check. user is most-likely passing an error code, and it would
+ // Validation. user is most-likely passing an error code, and it would
// make the return value ambiguous (actualSize vs error).
ALOGE("%s(%d) (buffer=%p, size=%zu (%zu)",
__func__, mPortId, buffer, userSize, userSize);
@@ -1319,7 +1319,7 @@
mCbf(EVENT_MORE_DATA, mUserData, &audioBuffer);
size_t readSize = audioBuffer.size;
- // Sanity check on returned size
+ // Validate on returned size
if (ssize_t(readSize) < 0 || readSize > reqSize) {
ALOGE("%s(%d): EVENT_MORE_DATA requested %zu bytes but callback returned %zd bytes",
__func__, mPortId, reqSize, ssize_t(readSize));
diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp
index 32129f0..807aa13 100644
--- a/media/libaudioclient/AudioTrack.cpp
+++ b/media/libaudioclient/AudioTrack.cpp
@@ -1936,7 +1936,7 @@
}
if (ssize_t(userSize) < 0 || (buffer == NULL && userSize != 0)) {
- // Sanity-check: user is most-likely passing an error code, and it would
+ // Validation: user is most-likely passing an error code, and it would
// make the return value ambiguous (actualSize vs error).
ALOGE("%s(%d): AudioTrack::write(buffer=%p, size=%zu (%zd)",
__func__, mPortId, buffer, userSize, userSize);
@@ -2326,7 +2326,7 @@
mUserData, &audioBuffer);
size_t writtenSize = audioBuffer.size;
- // Sanity check on returned size
+ // Validate on returned size
if (ssize_t(writtenSize) < 0 || writtenSize > reqSize) {
ALOGE("%s(%d): EVENT_MORE_DATA requested %zu bytes but callback returned %zd bytes",
__func__, mPortId, reqSize, ssize_t(writtenSize));
diff --git a/media/libaudioclient/include/media/AudioEffect.h b/media/libaudioclient/include/media/AudioEffect.h
index cb76252..3d4bb4e 100644
--- a/media/libaudioclient/include/media/AudioEffect.h
+++ b/media/libaudioclient/include/media/AudioEffect.h
@@ -339,16 +339,21 @@
*
* opPackageName: The package name used for app op checks.
*/
- AudioEffect(const String16& opPackageName);
+ explicit AudioEffect(const String16& opPackageName);
+ /* Terminates the AudioEffect and unregisters it from AudioFlinger.
+ * The effect engine is also destroyed if this AudioEffect was the last controlling
+ * the engine.
+ */
+ ~AudioEffect();
- /* Constructor.
+ /**
+ * Initialize an uninitialized AudioEffect.
*
* Parameters:
*
* type: type of effect created: can be null if uuid is specified. This corresponds to
* the OpenSL ES interface implemented by this effect.
- * opPackageName: The package name used for app op checks.
* uuid: Uuid of effect created: can be null if type is specified. This uuid corresponds to
* a particular implementation of an effect type.
* priority: requested priority for effect control: the priority level corresponds to the
@@ -356,7 +361,7 @@
* higher priorities, 0 being the normal priority.
* cbf: optional callback function (see effect_callback_t)
* user: pointer to context for use by the callback receiver.
- * sessionID: audio session this effect is associated to.
+ * sessionId: audio session this effect is associated to.
* If equal to AUDIO_SESSION_OUTPUT_MIX, the effect will be global to
* the output mix. Otherwise, the effect will be applied to all players
* (AudioTrack or MediaPLayer) within the same audio session.
@@ -369,46 +374,13 @@
* In this mode, no IEffect interface to AudioFlinger is created and all actions
* besides getters implemented in client AudioEffect object are no ops
* after effect creation.
+ *
+ * Returned status (from utils/Errors.h) can be:
+ * - NO_ERROR or ALREADY_EXISTS: successful initialization
+ * - INVALID_OPERATION: AudioEffect is already initialized
+ * - BAD_VALUE: invalid parameter
+ * - NO_INIT: audio flinger or audio hardware not initialized
*/
-
- AudioEffect(const effect_uuid_t *type,
- const String16& opPackageName,
- const effect_uuid_t *uuid = NULL,
- int32_t priority = 0,
- effect_callback_t cbf = NULL,
- void* user = NULL,
- audio_session_t sessionId = AUDIO_SESSION_OUTPUT_MIX,
- audio_io_handle_t io = AUDIO_IO_HANDLE_NONE,
- const AudioDeviceTypeAddr& device = {},
- bool probe = false);
-
- /* Constructor.
- * Same as above but with type and uuid specified by character strings
- */
- AudioEffect(const char *typeStr,
- const String16& opPackageName,
- const char *uuidStr = NULL,
- int32_t priority = 0,
- effect_callback_t cbf = NULL,
- void* user = NULL,
- audio_session_t sessionId = AUDIO_SESSION_OUTPUT_MIX,
- audio_io_handle_t io = AUDIO_IO_HANDLE_NONE,
- const AudioDeviceTypeAddr& device = {},
- bool probe = false);
-
- /* Terminates the AudioEffect and unregisters it from AudioFlinger.
- * The effect engine is also destroyed if this AudioEffect was the last controlling
- * the engine.
- */
- ~AudioEffect();
-
- /* Initialize an uninitialized AudioEffect.
- * Returned status (from utils/Errors.h) can be:
- * - NO_ERROR or ALREADY_EXISTS: successful initialization
- * - INVALID_OPERATION: AudioEffect is already initialized
- * - BAD_VALUE: invalid parameter
- * - NO_INIT: audio flinger or audio hardware not initialized
- * */
status_t set(const effect_uuid_t *type,
const effect_uuid_t *uuid = NULL,
int32_t priority = 0,
@@ -418,6 +390,18 @@
audio_io_handle_t io = AUDIO_IO_HANDLE_NONE,
const AudioDeviceTypeAddr& device = {},
bool probe = false);
+ /*
+ * Same as above but with type and uuid specified by character strings.
+ */
+ status_t set(const char *typeStr,
+ const char *uuidStr = NULL,
+ int32_t priority = 0,
+ effect_callback_t cbf = NULL,
+ void* user = NULL,
+ audio_session_t sessionId = AUDIO_SESSION_OUTPUT_MIX,
+ audio_io_handle_t io = AUDIO_IO_HANDLE_NONE,
+ const AudioDeviceTypeAddr& device = {},
+ bool probe = false);
/* Result of constructing the AudioEffect. This must be checked
* before using any AudioEffect API.
@@ -547,21 +531,20 @@
static const uint32_t kMaxPreProcessing = 10;
protected:
- bool mEnabled; // enable state
- audio_session_t mSessionId; // audio session ID
- int32_t mPriority; // priority for effect control
- status_t mStatus; // effect status
- bool mProbe; // effect created in probe mode: all commands
+ const String16 mOpPackageName; // The package name used for app op checks.
+ bool mEnabled = false; // enable state
+ audio_session_t mSessionId = AUDIO_SESSION_OUTPUT_MIX; // audio session ID
+ int32_t mPriority = 0; // priority for effect control
+ status_t mStatus = NO_INIT; // effect status
+ bool mProbe = false; // effect created in probe mode: all commands
// are no ops because mIEffect is NULL
- effect_callback_t mCbf; // callback function for status, control and
+ effect_callback_t mCbf = nullptr; // callback function for status, control and
// parameter changes notifications
- void* mUserData; // client context for callback function
- effect_descriptor_t mDescriptor; // effect descriptor
- int32_t mId; // system wide unique effect engine instance ID
+ void* mUserData = nullptr;// client context for callback function
+ effect_descriptor_t mDescriptor = {}; // effect descriptor
+ int32_t mId = -1; // system wide unique effect engine instance ID
Mutex mLock; // Mutex for mEnabled access
- Mutex mConstructLock; // Mutex for integrality construction
- String16 mOpPackageName; // The package name used for app op checks.
// IEffectClient
virtual void controlStatusChanged(bool controlGranted);
@@ -586,22 +569,12 @@
virtual void controlStatusChanged(bool controlGranted) {
sp<AudioEffect> effect = mEffect.promote();
if (effect != 0) {
- {
- // Got the mConstructLock means the construction of AudioEffect
- // has finished, we should release the mConstructLock immediately.
- AutoMutex lock(effect->mConstructLock);
- }
effect->controlStatusChanged(controlGranted);
}
}
virtual void enableStatusChanged(bool enabled) {
sp<AudioEffect> effect = mEffect.promote();
if (effect != 0) {
- {
- // Got the mConstructLock means the construction of AudioEffect
- // has finished, we should release the mConstructLock immediately.
- AutoMutex lock(effect->mConstructLock);
- }
effect->enableStatusChanged(enabled);
}
}
@@ -612,11 +585,6 @@
void *pReplyData) {
sp<AudioEffect> effect = mEffect.promote();
if (effect != 0) {
- {
- // Got the mConstructLock means the construction of AudioEffect
- // has finished, we should release the mConstructLock immediately.
- AutoMutex lock(effect->mConstructLock);
- }
effect->commandExecuted(
cmdCode, cmdSize, pCmdData, replySize, pReplyData);
}
@@ -626,11 +594,6 @@
virtual void binderDied(const wp<IBinder>& /*who*/) {
sp<AudioEffect> effect = mEffect.promote();
if (effect != 0) {
- {
- // Got the mConstructLock means the construction of AudioEffect
- // has finished, we should release the mConstructLock immediately.
- AutoMutex lock(effect->mConstructLock);
- }
effect->binderDied();
}
}
@@ -644,7 +607,7 @@
sp<IEffect> mIEffect; // IEffect binder interface
sp<EffectClient> mIEffectClient; // IEffectClient implementation
sp<IMemory> mCblkMemory; // shared memory for deferred parameter setting
- effect_param_cblk_t* mCblk; // control block for deferred parameter setting
+ effect_param_cblk_t* mCblk = nullptr; // control block for deferred parameter setting
pid_t mClientPid = (pid_t)-1;
uid_t mClientUid = (uid_t)-1;
};
diff --git a/media/libaudiofoundation/DeviceDescriptorBase.cpp b/media/libaudiofoundation/DeviceDescriptorBase.cpp
index 3dbe37d..e9b589d 100644
--- a/media/libaudiofoundation/DeviceDescriptorBase.cpp
+++ b/media/libaudiofoundation/DeviceDescriptorBase.cpp
@@ -22,6 +22,9 @@
#include <media/DeviceDescriptorBase.h>
#include <media/TypeConverter.h>
+#include <arpa/inet.h>
+#include <regex>
+
namespace android {
DeviceDescriptorBase::DeviceDescriptorBase(audio_devices_t type) :
@@ -34,6 +37,31 @@
{
}
+namespace {
+
+static const std::string SUPPRESSED = "SUPPRESSED";
+static const std::regex MAC_ADDRESS_REGEX("([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}");
+
+bool isAddressSensitive(const std::string &address) {
+ if (std::regex_match(address, MAC_ADDRESS_REGEX)) {
+ return true;
+ }
+
+ sockaddr_storage ss4;
+ if (inet_pton(AF_INET, address.c_str(), &ss4) > 0) {
+ return true;
+ }
+
+ sockaddr_storage ss6;
+ if (inet_pton(AF_INET6, address.c_str(), &ss6) > 0) {
+ return true;
+ }
+
+ return false;
+}
+
+} // namespace
+
DeviceDescriptorBase::DeviceDescriptorBase(const AudioDeviceTypeAddr &deviceTypeAddr) :
AudioPort("", AUDIO_PORT_TYPE_DEVICE,
audio_is_output_device(deviceTypeAddr.mType) ? AUDIO_PORT_ROLE_SINK :
@@ -43,6 +71,12 @@
if (mDeviceTypeAddr.mAddress.empty() && audio_is_remote_submix_device(mDeviceTypeAddr.mType)) {
mDeviceTypeAddr.mAddress = "0";
}
+ mIsAddressSensitive = isAddressSensitive(mDeviceTypeAddr.mAddress);
+}
+
+void DeviceDescriptorBase::setAddress(const std::string &address) {
+ mDeviceTypeAddr.mAddress = address;
+ mIsAddressSensitive = isAddressSensitive(address);
}
void DeviceDescriptorBase::toAudioPortConfig(struct audio_port_config *dstConfig,
@@ -130,10 +164,15 @@
AudioPort::dump(dst, spaces, verbose);
}
-std::string DeviceDescriptorBase::toString() const
+std::string DeviceDescriptorBase::toString(bool includeSensitiveInfo) const
{
std::stringstream sstream;
- sstream << "type:0x" << std::hex << type() << ",@:" << mDeviceTypeAddr.mAddress;
+ sstream << "type:0x" << std::hex << type();
+ // IP and MAC address are sensitive information. The sensitive information will be suppressed
+ // is `includeSensitiveInfo` is false.
+ sstream << ",@:"
+ << (!includeSensitiveInfo && mIsAddressSensitive ? SUPPRESSED
+ : mDeviceTypeAddr.mAddress);
return sstream.str();
}
diff --git a/media/libaudiofoundation/include/media/DeviceDescriptorBase.h b/media/libaudiofoundation/include/media/DeviceDescriptorBase.h
index af04721..c143c7e 100644
--- a/media/libaudiofoundation/include/media/DeviceDescriptorBase.h
+++ b/media/libaudiofoundation/include/media/DeviceDescriptorBase.h
@@ -42,7 +42,7 @@
audio_devices_t type() const { return mDeviceTypeAddr.mType; }
std::string address() const { return mDeviceTypeAddr.mAddress; }
- void setAddress(const std::string &address) { mDeviceTypeAddr.mAddress = address; }
+ void setAddress(const std::string &address);
const AudioDeviceTypeAddr& getDeviceTypeAddr() const { return mDeviceTypeAddr; }
// AudioPortConfig
@@ -61,7 +61,14 @@
void dump(std::string *dst, int spaces, int index,
const char* extraInfo = nullptr, bool verbose = true) const;
void log() const;
- std::string toString() const;
+
+ /**
+ * Return a string to describe the DeviceDescriptor.
+ *
+ * @param includeSensitiveInfo sensitive information will be added when it is true.
+ * @return a string that can be used to describe the DeviceDescriptor.
+ */
+ std::string toString(bool includeSensitiveInfo = false) const;
bool equals(const sp<DeviceDescriptorBase>& other) const;
@@ -70,6 +77,7 @@
protected:
AudioDeviceTypeAddr mDeviceTypeAddr;
+ bool mIsAddressSensitive;
uint32_t mEncapsulationModes = 0;
uint32_t mEncapsulationMetadataTypes = 0;
};
diff --git a/media/libaudioprocessing/AudioMixer.cpp b/media/libaudioprocessing/AudioMixer.cpp
index 1a31420..2ea215f 100644
--- a/media/libaudioprocessing/AudioMixer.cpp
+++ b/media/libaudioprocessing/AudioMixer.cpp
@@ -423,7 +423,7 @@
}
} break;
case HAPTIC_INTENSITY: {
- const haptic_intensity_t hapticIntensity = static_cast<haptic_intensity_t>(valueInt);
+ const os::HapticScale hapticIntensity = static_cast<os::HapticScale>(valueInt);
if (track->mHapticIntensity != hapticIntensity) {
track->mHapticIntensity = hapticIntensity;
}
@@ -545,7 +545,7 @@
t->mPlaybackRate = AUDIO_PLAYBACK_RATE_DEFAULT;
// haptic
t->mHapticPlaybackEnabled = false;
- t->mHapticIntensity = HAPTIC_SCALE_NONE;
+ t->mHapticIntensity = os::HapticScale::NONE;
t->mMixerHapticChannelMask = AUDIO_CHANNEL_NONE;
t->mMixerHapticChannelCount = 0;
t->mAdjustInChannelCount = t->channelCount + t->mHapticChannelCount;
@@ -590,19 +590,12 @@
const std::shared_ptr<Track> &t = getTrack(name);
if (t->mHapticPlaybackEnabled) {
size_t sampleCount = mFrameCount * t->mMixerHapticChannelCount;
- float gamma = t->getHapticScaleGamma();
- float maxAmplitudeRatio = t->getHapticMaxAmplitudeRatio();
uint8_t* buffer = (uint8_t*)pair.first + mFrameCount * audio_bytes_per_frame(
t->mMixerChannelCount, t->mMixerFormat);
switch (t->mMixerFormat) {
// Mixer format should be AUDIO_FORMAT_PCM_FLOAT.
case AUDIO_FORMAT_PCM_FLOAT: {
- float* fout = (float*) buffer;
- for (size_t i = 0; i < sampleCount; i++) {
- float mul = fout[i] >= 0 ? 1.0 : -1.0;
- fout[i] = powf(fabsf(fout[i] / HAPTIC_MAX_AMPLITUDE_FLOAT), gamma)
- * maxAmplitudeRatio * HAPTIC_MAX_AMPLITUDE_FLOAT * mul;
- }
+ os::scaleHapticData((float*) buffer, sampleCount, t->mHapticIntensity);
} break;
default:
LOG_ALWAYS_FATAL("bad mMixerFormat: %#x", t->mMixerFormat);
diff --git a/media/libaudioprocessing/AudioResamplerDyn.cpp b/media/libaudioprocessing/AudioResamplerDyn.cpp
index ec56b00..96d6104 100644
--- a/media/libaudioprocessing/AudioResamplerDyn.cpp
+++ b/media/libaudioprocessing/AudioResamplerDyn.cpp
@@ -636,7 +636,7 @@
const uint32_t phaseWrapLimit = c.mL << c.mShift;
size_t inFrameCount = (phaseIncrement * (uint64_t)outFrameCount + phaseFraction)
/ phaseWrapLimit;
- // sanity check that inFrameCount is in signed 32 bit integer range.
+ // validate that inFrameCount is in signed 32 bit integer range.
ALOG_ASSERT(0 <= inFrameCount && inFrameCount < (1U << 31));
//ALOGV("inFrameCount:%d outFrameCount:%d"
@@ -646,7 +646,7 @@
// NOTE: be very careful when modifying the code here. register
// pressure is very high and a small change might cause the compiler
// to generate far less efficient code.
- // Always sanity check the result with objdump or test-resample.
+ // Always validate the result with objdump or test-resample.
// the following logic is a bit convoluted to keep the main processing loop
// as tight as possible with register allocation.
diff --git a/media/libaudioprocessing/AudioResamplerFirProcess.h b/media/libaudioprocessing/AudioResamplerFirProcess.h
index 9b70a1c..1fcffcc 100644
--- a/media/libaudioprocessing/AudioResamplerFirProcess.h
+++ b/media/libaudioprocessing/AudioResamplerFirProcess.h
@@ -381,7 +381,7 @@
// NOTE: be very careful when modifying the code here. register
// pressure is very high and a small change might cause the compiler
// to generate far less efficient code.
- // Always sanity check the result with objdump or test-resample.
+ // Always validate the result with objdump or test-resample.
if (LOCKED) {
// locked polyphase (no interpolation)
diff --git a/media/libaudioprocessing/AudioResamplerSinc.cpp b/media/libaudioprocessing/AudioResamplerSinc.cpp
index 5a03a0d..f2c386d 100644
--- a/media/libaudioprocessing/AudioResamplerSinc.cpp
+++ b/media/libaudioprocessing/AudioResamplerSinc.cpp
@@ -404,7 +404,7 @@
// NOTE: be very careful when modifying the code here. register
// pressure is very high and a small change might cause the compiler
// to generate far less efficient code.
- // Always sanity check the result with objdump or test-resample.
+ // Always validate the result with objdump or test-resample.
// compute the index of the coefficient on the positive side and
// negative side
diff --git a/media/libaudioprocessing/include/media/AudioMixer.h b/media/libaudioprocessing/include/media/AudioMixer.h
index 3f7cd48..70eafe3 100644
--- a/media/libaudioprocessing/include/media/AudioMixer.h
+++ b/media/libaudioprocessing/include/media/AudioMixer.h
@@ -22,10 +22,10 @@
#include <stdint.h>
#include <sys/types.h>
-#include <android/os/IExternalVibratorService.h>
#include <media/AudioMixerBase.h>
#include <media/BufferProviders.h>
#include <utils/threads.h>
+#include <vibrator/ExternalVibrationUtils.h>
// FIXME This is actually unity gain, which might not be max in future, expressed in U.12
#define MAX_GAIN_INT AudioMixerBase::UNITY_GAIN_INT
@@ -55,32 +55,6 @@
// parameter 'value' is a pointer to the new playback rate.
};
- typedef enum { // Haptic intensity, should keep consistent with VibratorService
- HAPTIC_SCALE_MUTE = os::IExternalVibratorService::SCALE_MUTE,
- HAPTIC_SCALE_VERY_LOW = os::IExternalVibratorService::SCALE_VERY_LOW,
- HAPTIC_SCALE_LOW = os::IExternalVibratorService::SCALE_LOW,
- HAPTIC_SCALE_NONE = os::IExternalVibratorService::SCALE_NONE,
- HAPTIC_SCALE_HIGH = os::IExternalVibratorService::SCALE_HIGH,
- HAPTIC_SCALE_VERY_HIGH = os::IExternalVibratorService::SCALE_VERY_HIGH,
- } haptic_intensity_t;
- static constexpr float HAPTIC_SCALE_VERY_LOW_RATIO = 2.0f / 3.0f;
- static constexpr float HAPTIC_SCALE_LOW_RATIO = 3.0f / 4.0f;
- static const constexpr float HAPTIC_MAX_AMPLITUDE_FLOAT = 1.0f;
-
- static inline bool isValidHapticIntensity(haptic_intensity_t hapticIntensity) {
- switch (hapticIntensity) {
- case HAPTIC_SCALE_MUTE:
- case HAPTIC_SCALE_VERY_LOW:
- case HAPTIC_SCALE_LOW:
- case HAPTIC_SCALE_NONE:
- case HAPTIC_SCALE_HIGH:
- case HAPTIC_SCALE_VERY_HIGH:
- return true;
- default:
- return false;
- }
- }
-
AudioMixer(size_t frameCount, uint32_t sampleRate)
: AudioMixerBase(frameCount, sampleRate) {
pthread_once(&sOnceControl, &sInitRoutine);
@@ -170,7 +144,7 @@
// Haptic
bool mHapticPlaybackEnabled;
- haptic_intensity_t mHapticIntensity;
+ os::HapticScale mHapticIntensity;
audio_channel_mask_t mHapticChannelMask;
uint32_t mHapticChannelCount;
audio_channel_mask_t mMixerHapticChannelMask;
@@ -180,38 +154,6 @@
uint32_t mAdjustNonDestructiveInChannelCount;
uint32_t mAdjustNonDestructiveOutChannelCount;
bool mKeepContractedChannels;
-
- float getHapticScaleGamma() const {
- // Need to keep consistent with the value in VibratorService.
- switch (mHapticIntensity) {
- case HAPTIC_SCALE_VERY_LOW:
- return 2.0f;
- case HAPTIC_SCALE_LOW:
- return 1.5f;
- case HAPTIC_SCALE_HIGH:
- return 0.5f;
- case HAPTIC_SCALE_VERY_HIGH:
- return 0.25f;
- default:
- return 1.0f;
- }
- }
-
- float getHapticMaxAmplitudeRatio() const {
- // Need to keep consistent with the value in VibratorService.
- switch (mHapticIntensity) {
- case HAPTIC_SCALE_VERY_LOW:
- return HAPTIC_SCALE_VERY_LOW_RATIO;
- case HAPTIC_SCALE_LOW:
- return HAPTIC_SCALE_LOW_RATIO;
- case HAPTIC_SCALE_NONE:
- case HAPTIC_SCALE_HIGH:
- case HAPTIC_SCALE_VERY_HIGH:
- return 1.0f;
- default:
- return 0.0f;
- }
- }
};
inline std::shared_ptr<Track> getTrack(int name) {
diff --git a/media/libeffects/factory/EffectsConfigLoader.c b/media/libeffects/factory/EffectsConfigLoader.c
index fcef36f..e23530e 100644
--- a/media/libeffects/factory/EffectsConfigLoader.c
+++ b/media/libeffects/factory/EffectsConfigLoader.c
@@ -394,7 +394,7 @@
}
sub_effect_entry_t *subEntry = (sub_effect_entry_t*)gSubEffectList->sub_elem->object;
effect_descriptor_t *subEffectDesc = (effect_descriptor_t*)(subEntry->object);
- // Since we return a dummy descriptor for the proxy during
+ // Since we return a stub descriptor for the proxy during
// get_descriptor call,we replace it with the correspoding
// sw effect descriptor, but with Proxy UUID
// check for Sw desc
diff --git a/media/libeffects/factory/EffectsXmlConfigLoader.cpp b/media/libeffects/factory/EffectsXmlConfigLoader.cpp
index 505be7c..30a9007 100644
--- a/media/libeffects/factory/EffectsXmlConfigLoader.cpp
+++ b/media/libeffects/factory/EffectsXmlConfigLoader.cpp
@@ -283,7 +283,7 @@
}
listPush(effectLoadResult.effectDesc.get(), subEffectList);
- // Since we return a dummy descriptor for the proxy during
+ // Since we return a stub descriptor for the proxy during
// get_descriptor call, we replace it with the corresponding
// sw effect descriptor, but keep the Proxy UUID
*effectLoadResult.effectDesc = *swEffectLoadResult.effectDesc;
diff --git a/media/libeffects/hapticgenerator/Android.bp b/media/libeffects/hapticgenerator/Android.bp
index ac40e33..f947339 100644
--- a/media/libeffects/hapticgenerator/Android.bp
+++ b/media/libeffects/hapticgenerator/Android.bp
@@ -36,8 +36,10 @@
shared_libs: [
"libaudioutils",
+ "libbinder",
"liblog",
"libutils",
+ "libvibrator",
],
relative_install_path: "soundfx",
diff --git a/media/libeffects/hapticgenerator/EffectHapticGenerator.cpp b/media/libeffects/hapticgenerator/EffectHapticGenerator.cpp
index 2a41ed5..311e5ba 100644
--- a/media/libeffects/hapticgenerator/EffectHapticGenerator.cpp
+++ b/media/libeffects/hapticgenerator/EffectHapticGenerator.cpp
@@ -96,7 +96,10 @@
context->config.outputCfg.bufferProvider.cookie = nullptr;
context->config.outputCfg.mask = EFFECT_CONFIG_ALL;
- memset(&context->param, 0, sizeof(struct HapticGeneratorParam));
+ memset(context->param.hapticChannelSource, 0, sizeof(context->param.hapticChannelSource));
+ context->param.hapticChannelCount = 0;
+ context->param.audioChannelCount = 0;
+ context->param.maxHapticIntensity = os::HapticScale::MUTE;
context->state = HAPTICGENERATOR_STATE_INITIALIZED;
return 0;
@@ -236,12 +239,36 @@
return 0;
}
-int HapticGenerator_SetParameter(struct HapticGeneratorContext *context __unused,
- int32_t param __unused,
- uint32_t size __unused,
- void *value __unused) {
- ALOGW("Setparameter is not implemented in HapticGenerator");
- return -ENOSYS;
+int HapticGenerator_SetParameter(struct HapticGeneratorContext *context,
+ int32_t param,
+ uint32_t size,
+ void *value) {
+ switch (param) {
+ case HG_PARAM_HAPTIC_INTENSITY: {
+ if (value == nullptr || size != (uint32_t) (2 * sizeof(int))) {
+ return -EINVAL;
+ }
+ int id = *(int *) value;
+ os::HapticScale hapticIntensity = static_cast<os::HapticScale>(*((int *) value + 1));
+ if (hapticIntensity == os::HapticScale::MUTE) {
+ context->param.id2Intensity.erase(id);
+ } else {
+ context->param.id2Intensity.emplace(id, hapticIntensity);
+ }
+ context->param.maxHapticIntensity = hapticIntensity;
+ for (const auto&[id, intensity] : context->param.id2Intensity) {
+ context->param.maxHapticIntensity = std::max(
+ context->param.maxHapticIntensity, intensity);
+ }
+ break;
+ }
+
+ default:
+ ALOGW("Unknown param: %d", param);
+ return -EINVAL;
+ }
+
+ return 0;
}
/**
@@ -346,6 +373,11 @@
return -ENODATA;
}
+ if (context->param.maxHapticIntensity == os::HapticScale::MUTE) {
+ // Haptic channels are muted, not need to generate haptic data.
+ return 0;
+ }
+
// Resize buffer if the haptic sample count is greater than buffer size.
size_t hapticSampleCount = inBuffer->frameCount * context->param.hapticChannelCount;
if (hapticSampleCount > context->inputBuffer.size()) {
@@ -367,6 +399,7 @@
float* hapticOutBuffer = HapticGenerator_runProcessingChain(
context->processingChain, context->inputBuffer.data(),
context->outputBuffer.data(), inBuffer->frameCount);
+ os::scaleHapticData(hapticOutBuffer, hapticSampleCount, context->param.maxHapticIntensity);
// For haptic data, the haptic playback thread will copy the data from effect input buffer,
// which contains haptic data at the end of the buffer, directly to sink buffer.
diff --git a/media/libeffects/hapticgenerator/EffectHapticGenerator.h b/media/libeffects/hapticgenerator/EffectHapticGenerator.h
index 4a2308b..a5688de 100644
--- a/media/libeffects/hapticgenerator/EffectHapticGenerator.h
+++ b/media/libeffects/hapticgenerator/EffectHapticGenerator.h
@@ -19,9 +19,11 @@
#include <functional>
#include <vector>
+#include <map>
#include <hardware/audio_effect.h>
#include <system/audio_effect.h>
+#include <vibrator/ExternalVibrationUtils.h>
#include "Processors.h"
@@ -45,6 +47,10 @@
// The value will be offset of audio channel
uint32_t audioChannelCount;
uint32_t hapticChannelCount;
+
+ // A map from track id to haptic intensity.
+ std::map<int, os::HapticScale> id2Intensity;
+ os::HapticScale maxHapticIntensity; // max intensity will be used to scale haptic data.
};
// A structure to keep all shared pointers for all processors in HapticGenerator.
diff --git a/media/libeffects/proxy/EffectProxy.cpp b/media/libeffects/proxy/EffectProxy.cpp
index 42e44f0..c010d68 100644
--- a/media/libeffects/proxy/EffectProxy.cpp
+++ b/media/libeffects/proxy/EffectProxy.cpp
@@ -30,7 +30,7 @@
#include <media/EffectsFactoryApi.h>
namespace android {
-// This is a dummy proxy descriptor just to return to Factory during the initial
+// This is a stub proxy descriptor just to return to Factory during the initial
// GetDescriptor call. Later in the factory, it is replaced with the
// SW sub effect descriptor
// proxy UUID af8da7e0-2ca1-11e3-b71d-0002a5d5c51b
diff --git a/media/libmediaplayerservice/tests/stagefrightRecorder/Android.bp b/media/libmediaplayerservice/tests/stagefrightRecorder/Android.bp
new file mode 100644
index 0000000..5a52ea5
--- /dev/null
+++ b/media/libmediaplayerservice/tests/stagefrightRecorder/Android.bp
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+
+cc_test {
+ name: "StagefrightRecorderTest",
+ gtest: true,
+
+ srcs: [
+ "StagefrightRecorderTest.cpp",
+ ],
+
+ include_dirs: [
+ "system/media/audio/include",
+ "frameworks/av/include",
+ "frameworks/av/camera/include",
+ "frameworks/av/media/libmediaplayerservice",
+ "frameworks/av/media/libmediametrics/include",
+ "frameworks/av/media/ndk/include",
+ ],
+
+ shared_libs: [
+ "liblog",
+ "libmedia",
+ "libbinder",
+ "libutils",
+ "libmediaplayerservice",
+ "libstagefright",
+ "libmediandk",
+ ],
+
+ compile_multilib: "32",
+
+ cflags: [
+ "-Werror",
+ "-Wall",
+ ],
+
+ sanitize: {
+ cfi: true,
+ misc_undefined: [
+ "unsigned-integer-overflow",
+ "signed-integer-overflow",
+ ],
+ },
+}
diff --git a/media/libmediaplayerservice/tests/stagefrightRecorder/StagefrightRecorderTest.cpp b/media/libmediaplayerservice/tests/stagefrightRecorder/StagefrightRecorderTest.cpp
new file mode 100644
index 0000000..ac17ef3
--- /dev/null
+++ b/media/libmediaplayerservice/tests/stagefrightRecorder/StagefrightRecorderTest.cpp
@@ -0,0 +1,318 @@
+/*
+ * 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 "StagefrightRecorderTest"
+#include <utils/Log.h>
+
+#include <gtest/gtest.h>
+
+#include <chrono>
+#include <ctime>
+#include <iostream>
+#include <string>
+#include <thread>
+
+#include <MediaPlayerService.h>
+#include <media/NdkMediaExtractor.h>
+#include <media/stagefright/MediaCodec.h>
+#include <system/audio-base.h>
+
+#include "StagefrightRecorder.h"
+
+#define OUTPUT_INFO_FILE_NAME "/data/local/tmp/stfrecorder_audio.info"
+#define OUTPUT_FILE_NAME_AUDIO "/data/local/tmp/stfrecorder_audio.raw"
+
+const bool kDebug = false;
+constexpr int32_t kMaxLoopCount = 10;
+constexpr int32_t kClipDurationInSec = 4;
+constexpr int32_t kPauseTimeInSec = 2;
+// Tolerance value for extracted clipduration is maximum 10% of total clipduration
+constexpr int32_t kToleranceValueInUs = kClipDurationInSec * 100000;
+
+using namespace android;
+
+class StagefrightRecorderTest
+ : public ::testing::TestWithParam<std::pair<output_format, audio_encoder>> {
+ public:
+ StagefrightRecorderTest() : mStfRecorder(nullptr), mOutputAudioFp(nullptr) {
+ mExpectedDurationInMs = 0;
+ mExpectedPauseInMs = 0;
+ }
+
+ ~StagefrightRecorderTest() {
+ if (mStfRecorder) free(mStfRecorder);
+ if (mOutputAudioFp) fclose(mOutputAudioFp);
+ }
+
+ void SetUp() override {
+ mStfRecorder = new StagefrightRecorder(String16(LOG_TAG));
+ ASSERT_NE(mStfRecorder, nullptr) << "Failed to create the instance of recorder";
+
+ mOutputAudioFp = fopen(OUTPUT_FILE_NAME_AUDIO, "wb");
+ ASSERT_NE(mOutputAudioFp, nullptr) << "Failed to open output file "
+ << OUTPUT_FILE_NAME_AUDIO << " for stagefright recorder";
+
+ int32_t fd = fileno(mOutputAudioFp);
+ ASSERT_GE(fd, 0) << "Failed to get the file descriptor of the output file for "
+ << OUTPUT_FILE_NAME_AUDIO;
+
+ status_t status = mStfRecorder->setOutputFile(fd);
+ ASSERT_EQ(status, OK) << "Failed to set the output file " << OUTPUT_FILE_NAME_AUDIO
+ << " for stagefright recorder";
+ }
+
+ void TearDown() override {
+ if (mOutputAudioFp) {
+ fclose(mOutputAudioFp);
+ mOutputAudioFp = nullptr;
+ }
+ if (!kDebug) {
+ int32_t status = remove(OUTPUT_FILE_NAME_AUDIO);
+ ASSERT_EQ(status, 0) << "Unable to delete the output file " << OUTPUT_FILE_NAME_AUDIO;
+ }
+ }
+
+ void setAudioRecorderFormat(output_format outputFormat, audio_encoder encoder,
+ audio_source_t audioSource = AUDIO_SOURCE_DEFAULT);
+ void recordMedia(bool isPaused = false, int32_t numStart = 0, int32_t numPause = 0);
+ void dumpInfo();
+ void setupExtractor(AMediaExtractor *extractor, int32_t &trackCount);
+ void validateOutput();
+
+ MediaRecorderBase *mStfRecorder;
+ FILE *mOutputAudioFp;
+ double mExpectedDurationInMs;
+ double mExpectedPauseInMs;
+};
+
+void StagefrightRecorderTest::setAudioRecorderFormat(output_format outputFormat,
+ audio_encoder encoder,
+ audio_source_t audioSource) {
+ status_t status = mStfRecorder->setAudioSource(audioSource);
+ ASSERT_EQ(status, OK) << "Failed to set the audio source: " << audioSource;
+
+ status = mStfRecorder->setOutputFormat(outputFormat);
+ ASSERT_EQ(status, OK) << "Failed to set the output format: " << outputFormat;
+
+ status = mStfRecorder->setAudioEncoder(encoder);
+ ASSERT_EQ(status, OK) << "Failed to set the audio encoder: " << encoder;
+}
+
+void StagefrightRecorderTest::recordMedia(bool isPause, int32_t numStart, int32_t numPause) {
+ status_t status = mStfRecorder->init();
+ ASSERT_EQ(status, OK) << "Failed to initialize stagefright recorder";
+
+ status = mStfRecorder->prepare();
+ ASSERT_EQ(status, OK) << "Failed to preapre the reorder";
+
+ // first start should succeed.
+ status = mStfRecorder->start();
+ ASSERT_EQ(status, OK) << "Failed to start the recorder";
+
+ for (int32_t count = 0; count < numStart; count++) {
+ status = mStfRecorder->start();
+ }
+
+ auto tStart = std::chrono::high_resolution_clock::now();
+ // Recording media for 4 secs
+ std::this_thread::sleep_for(std::chrono::seconds(kClipDurationInSec));
+ auto tEnd = std::chrono::high_resolution_clock::now();
+ mExpectedDurationInMs = std::chrono::duration<double, std::milli>(tEnd - tStart).count();
+
+ if (isPause) {
+ // first pause should succeed.
+ status = mStfRecorder->pause();
+ ASSERT_EQ(status, OK) << "Failed to pause the recorder";
+
+ tStart = std::chrono::high_resolution_clock::now();
+ // Paused recorder for 2 secs
+ std::this_thread::sleep_for(std::chrono::seconds(kPauseTimeInSec));
+
+ for (int32_t count = 0; count < numPause; count++) {
+ status = mStfRecorder->pause();
+ }
+
+ tEnd = std::chrono::high_resolution_clock::now();
+ mExpectedPauseInMs = std::chrono::duration<double, std::milli>(tEnd - tStart).count();
+
+ status = mStfRecorder->resume();
+ ASSERT_EQ(status, OK) << "Failed to resume the recorder";
+
+ auto tStart = std::chrono::high_resolution_clock::now();
+ // Recording media for 4 secs
+ std::this_thread::sleep_for(std::chrono::seconds(kClipDurationInSec));
+ auto tEnd = std::chrono::high_resolution_clock::now();
+ mExpectedDurationInMs += std::chrono::duration<double, std::milli>(tEnd - tStart).count();
+ }
+ status = mStfRecorder->stop();
+ ASSERT_EQ(status, OK) << "Failed to stop the recorder";
+}
+
+void StagefrightRecorderTest::dumpInfo() {
+ FILE *dumpOutput = fopen(OUTPUT_INFO_FILE_NAME, "wb");
+ int32_t dumpFd = fileno(dumpOutput);
+ Vector<String16> args;
+ status_t status = mStfRecorder->dump(dumpFd, args);
+ ASSERT_EQ(status, OK) << "Failed to dump the info for the recorder";
+ fclose(dumpOutput);
+}
+
+void StagefrightRecorderTest::setupExtractor(AMediaExtractor *extractor, int32_t &trackCount) {
+ int32_t fd = open(OUTPUT_FILE_NAME_AUDIO, O_RDONLY);
+ ASSERT_GE(fd, 0) << "Failed to open recorder's output file " << OUTPUT_FILE_NAME_AUDIO
+ << " to validate";
+
+ struct stat buf;
+ int32_t status = fstat(fd, &buf);
+ ASSERT_EQ(status, 0) << "Failed to get properties of input file " << OUTPUT_FILE_NAME_AUDIO
+ << " for extractor";
+
+ size_t fileSize = buf.st_size;
+ ASSERT_GT(fileSize, 0) << "Size of input file " << OUTPUT_FILE_NAME_AUDIO
+ << " to extractor cannot be zero";
+ ALOGV("Size of input file to extractor: %zu", fileSize);
+
+ status = AMediaExtractor_setDataSourceFd(extractor, fd, 0, fileSize);
+ ASSERT_EQ(status, AMEDIA_OK) << "Failed to set data source for extractor";
+
+ trackCount = AMediaExtractor_getTrackCount(extractor);
+ ALOGV("Number of tracks reported by extractor : %d", trackCount);
+}
+
+// Validate recoder's output using extractor
+void StagefrightRecorderTest::validateOutput() {
+ int32_t trackCount = -1;
+ AMediaExtractor *extractor = AMediaExtractor_new();
+ ASSERT_NE(extractor, nullptr) << "Failed to create extractor";
+ ASSERT_NO_FATAL_FAILURE(setupExtractor(extractor, trackCount));
+ ASSERT_EQ(trackCount, 1) << "Expected 1 track, saw " << trackCount;
+
+ for (int32_t idx = 0; idx < trackCount; idx++) {
+ AMediaExtractor_selectTrack(extractor, idx);
+ AMediaFormat *format = AMediaExtractor_getTrackFormat(extractor, idx);
+ ASSERT_NE(format, nullptr) << "Track format is NULL";
+ ALOGI("Track format = %s", AMediaFormat_toString(format));
+
+ int64_t clipDurationUs;
+ AMediaFormat_getInt64(format, AMEDIAFORMAT_KEY_DURATION, &clipDurationUs);
+ int32_t diff = abs((mExpectedDurationInMs * 1000) - clipDurationUs);
+ ASSERT_LE(diff, kToleranceValueInUs)
+ << "Expected duration: " << (mExpectedDurationInMs * 1000)
+ << " Actual duration: " << clipDurationUs << " Difference: " << diff
+ << " Difference is expected to be less than tolerance value: " << kToleranceValueInUs;
+
+ const char *mime = nullptr;
+ AMediaFormat_getString(format, AMEDIAFORMAT_KEY_MIME, &mime);
+ ASSERT_NE(mime, nullptr) << "Track mime is NULL";
+ ALOGI("Track mime = %s", mime);
+
+ int32_t sampleRate, channelCount, bitRate;
+ AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_CHANNEL_COUNT, &channelCount);
+ ALOGI("Channel count reported by extractor: %d", channelCount);
+ AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_SAMPLE_RATE, &sampleRate);
+ ALOGI("Sample Rate reported by extractor: %d", sampleRate);
+ AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_BIT_RATE, &bitRate);
+ ALOGI("Bit Rate reported by extractor: %d", bitRate);
+ }
+}
+
+TEST_F(StagefrightRecorderTest, RecordingAudioSanityTest) {
+ ASSERT_NO_FATAL_FAILURE(setAudioRecorderFormat(OUTPUT_FORMAT_DEFAULT, AUDIO_ENCODER_DEFAULT));
+
+ int32_t maxAmplitude = -1;
+ status_t status = mStfRecorder->getMaxAmplitude(&maxAmplitude);
+ ASSERT_EQ(maxAmplitude, 0) << "Invalid value of max amplitude";
+
+ ASSERT_NO_FATAL_FAILURE(recordMedia());
+
+ // Verify getMetrics() behavior
+ Parcel parcel;
+ status = mStfRecorder->getMetrics(&parcel);
+ ASSERT_EQ(status, OK) << "Failed to get the parcel from getMetrics";
+ ALOGV("Size of the Parcel returned by getMetrics: %zu", parcel.dataSize());
+ ASSERT_GT(parcel.dataSize(), 0) << "Parcel size reports empty record";
+ ASSERT_NO_FATAL_FAILURE(validateOutput());
+ if (kDebug) {
+ ASSERT_NO_FATAL_FAILURE(dumpInfo());
+ }
+}
+
+TEST_P(StagefrightRecorderTest, MultiFormatAudioRecordTest) {
+ output_format outputFormat = GetParam().first;
+ audio_encoder audioEncoder = GetParam().second;
+ ASSERT_NO_FATAL_FAILURE(setAudioRecorderFormat(outputFormat, audioEncoder));
+ ASSERT_NO_FATAL_FAILURE(recordMedia());
+ // TODO(b/161687761)
+ // Skip for AMR-NB/WB output format
+ if (!(outputFormat == OUTPUT_FORMAT_AMR_NB || outputFormat == OUTPUT_FORMAT_AMR_WB)) {
+ ASSERT_NO_FATAL_FAILURE(validateOutput());
+ }
+ if (kDebug) {
+ ASSERT_NO_FATAL_FAILURE(dumpInfo());
+ }
+}
+
+TEST_F(StagefrightRecorderTest, GetActiveMicrophonesTest) {
+ ASSERT_NO_FATAL_FAILURE(
+ setAudioRecorderFormat(OUTPUT_FORMAT_DEFAULT, AUDIO_ENCODER_DEFAULT, AUDIO_SOURCE_MIC));
+
+ status_t status = mStfRecorder->init();
+ ASSERT_EQ(status, OK) << "Init failed for stagefright recorder";
+
+ status = mStfRecorder->prepare();
+ ASSERT_EQ(status, OK) << "Failed to preapre the reorder";
+
+ status = mStfRecorder->start();
+ ASSERT_EQ(status, OK) << "Failed to start the recorder";
+
+ // Record media for 4 secs
+ std::this_thread::sleep_for(std::chrono::seconds(kClipDurationInSec));
+
+ std::vector<media::MicrophoneInfo> activeMicrophones{};
+ status = mStfRecorder->getActiveMicrophones(&activeMicrophones);
+ ASSERT_EQ(status, OK) << "Failed to get Active Microphones";
+ ASSERT_GT(activeMicrophones.size(), 0) << "No active microphones are found";
+
+ status = mStfRecorder->stop();
+ ASSERT_EQ(status, OK) << "Failed to stop the recorder";
+ if (kDebug) {
+ ASSERT_NO_FATAL_FAILURE(dumpInfo());
+ }
+}
+
+TEST_F(StagefrightRecorderTest, MultiStartPauseTest) {
+ ASSERT_NO_FATAL_FAILURE(setAudioRecorderFormat(OUTPUT_FORMAT_DEFAULT, AUDIO_ENCODER_DEFAULT));
+ ASSERT_NO_FATAL_FAILURE(recordMedia(true, kMaxLoopCount, kMaxLoopCount));
+ ASSERT_NO_FATAL_FAILURE(validateOutput());
+ if (kDebug) {
+ ASSERT_NO_FATAL_FAILURE(dumpInfo());
+ }
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ StagefrightRecorderTestAll, StagefrightRecorderTest,
+ ::testing::Values(std::make_pair(OUTPUT_FORMAT_AMR_NB, AUDIO_ENCODER_AMR_NB),
+ std::make_pair(OUTPUT_FORMAT_AMR_WB, AUDIO_ENCODER_AMR_WB),
+ std::make_pair(OUTPUT_FORMAT_AAC_ADTS, AUDIO_ENCODER_AAC),
+ std::make_pair(OUTPUT_FORMAT_OGG, AUDIO_ENCODER_OPUS)));
+
+int main(int argc, char **argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ int status = RUN_ALL_TESTS();
+ ALOGV("Test result = %d\n", status);
+ return status;
+}
diff --git a/media/libmediatranscoding/transcoder/MediaSampleQueue.cpp b/media/libmediatranscoding/transcoder/MediaSampleQueue.cpp
index 691ee1c..b085c98 100644
--- a/media/libmediatranscoding/transcoder/MediaSampleQueue.cpp
+++ b/media/libmediatranscoding/transcoder/MediaSampleQueue.cpp
@@ -47,6 +47,11 @@
return mAborted;
}
+bool MediaSampleQueue::isEmpty() {
+ std::scoped_lock<std::mutex> lock(mMutex);
+ return mSampleQueue.empty();
+}
+
void MediaSampleQueue::abort() {
std::scoped_lock<std::mutex> lock(mMutex);
// Clear the queue and notify consumers.
diff --git a/media/libmediatranscoding/transcoder/MediaSampleWriter.cpp b/media/libmediatranscoding/transcoder/MediaSampleWriter.cpp
index 3676d73..bb0da88 100644
--- a/media/libmediatranscoding/transcoder/MediaSampleWriter.cpp
+++ b/media/libmediatranscoding/transcoder/MediaSampleWriter.cpp
@@ -127,18 +127,16 @@
durationUs = 0;
}
- const char* mime = nullptr;
- const bool isVideo = AMediaFormat_getString(trackFormat.get(), AMEDIAFORMAT_KEY_MIME, &mime) &&
- (strncmp(mime, "video/", 6) == 0);
-
- mTracks.emplace_back(sampleQueue, static_cast<size_t>(trackIndex), durationUs, isVideo);
+ mAllTracks.push_back(std::make_unique<TrackRecord>(sampleQueue, static_cast<size_t>(trackIndex),
+ durationUs));
+ mSortedTracks.insert(mAllTracks.back().get());
return true;
}
bool MediaSampleWriter::start() {
std::scoped_lock lock(mStateMutex);
- if (mTracks.size() == 0) {
+ if (mAllTracks.size() == 0) {
LOG(ERROR) << "No tracks to write.";
return false;
} else if (mState != INITIALIZED) {
@@ -165,8 +163,8 @@
}
// Stop the sources, and wait for thread to join.
- for (auto& track : mTracks) {
- track.mSampleQueue->abort();
+ for (auto& track : mAllTracks) {
+ track->mSampleQueue->abort();
}
mThread.join();
mState = STOPPED;
@@ -193,76 +191,102 @@
return writeStatus != AMEDIA_OK ? writeStatus : muxerStatus;
}
+std::multiset<MediaSampleWriter::TrackRecord*>::iterator MediaSampleWriter::getNextOutputTrack() {
+ // Find the first track that has samples ready in its queue AND is not more than
+ // mMaxTrackDivergenceUs ahead of the slowest track. If no such track exists then return the
+ // slowest track and let the writer wait for samples to become ready. Note that mSortedTracks is
+ // sorted by each track's previous sample timestamp in ascending order.
+ auto slowestTrack = mSortedTracks.begin();
+ if (slowestTrack == mSortedTracks.end() || !(*slowestTrack)->mSampleQueue->isEmpty()) {
+ return slowestTrack;
+ }
+
+ const int64_t slowestTimeUs = (*slowestTrack)->mPrevSampleTimeUs;
+ int64_t divergenceUs;
+
+ for (auto it = std::next(slowestTrack); it != mSortedTracks.end(); ++it) {
+ // If the current track has diverged then the rest will have too, so we can stop the search.
+ // If not and it has samples ready then return it, otherwise keep looking.
+ if (__builtin_sub_overflow((*it)->mPrevSampleTimeUs, slowestTimeUs, &divergenceUs) ||
+ divergenceUs >= mMaxTrackDivergenceUs) {
+ break;
+ } else if (!(*it)->mSampleQueue->isEmpty()) {
+ return it;
+ }
+ }
+
+ // No track with pending samples within acceptable time interval was found, so let the writer
+ // wait for the slowest track to produce a new sample.
+ return slowestTrack;
+}
+
media_status_t MediaSampleWriter::runWriterLoop() {
AMediaCodecBufferInfo bufferInfo;
- uint32_t segmentEndTimeUs = mTrackSegmentLengthUs;
- bool samplesLeft = true;
int32_t lastProgressUpdate = 0;
// Set the "primary" track that will be used to determine progress to the track with longest
// duration.
int primaryTrackIndex = -1;
int64_t longestDurationUs = 0;
- for (int trackIndex = 0; trackIndex < mTracks.size(); ++trackIndex) {
- if (mTracks[trackIndex].mDurationUs > longestDurationUs) {
- primaryTrackIndex = trackIndex;
- longestDurationUs = mTracks[trackIndex].mDurationUs;
+ for (auto& track : mAllTracks) {
+ if (track->mDurationUs > longestDurationUs) {
+ primaryTrackIndex = track->mTrackIndex;
+ longestDurationUs = track->mDurationUs;
}
}
- while (samplesLeft) {
- samplesLeft = false;
- for (auto& track : mTracks) {
- if (track.mReachedEos) continue;
+ while (true) {
+ auto outputTrackIter = getNextOutputTrack();
- std::shared_ptr<MediaSample> sample;
- do {
- if (track.mSampleQueue->dequeue(&sample)) {
- // Track queue was aborted.
- return AMEDIA_ERROR_UNKNOWN; // TODO(lnilsson): Custom error code.
- } else if (sample->info.flags & SAMPLE_FLAG_END_OF_STREAM) {
- // Track reached end of stream.
- track.mReachedEos = true;
-
- // Preserve source track duration by setting the appropriate timestamp on the
- // empty End-Of-Stream sample.
- if (track.mDurationUs > 0 && track.mFirstSampleTimeSet) {
- sample->info.presentationTimeUs =
- track.mDurationUs + track.mFirstSampleTimeUs;
- }
- } else {
- samplesLeft = true;
- }
-
- track.mPrevSampleTimeUs = sample->info.presentationTimeUs;
- if (!track.mFirstSampleTimeSet) {
- // Record the first sample's timestamp in order to translate duration to EOS
- // time for tracks that does not start at 0.
- track.mFirstSampleTimeUs = sample->info.presentationTimeUs;
- track.mFirstSampleTimeSet = true;
- }
-
- bufferInfo.offset = sample->dataOffset;
- bufferInfo.size = sample->info.size;
- bufferInfo.flags = sample->info.flags;
- bufferInfo.presentationTimeUs = sample->info.presentationTimeUs;
-
- media_status_t status =
- mMuxer->writeSampleData(track.mTrackIndex, sample->buffer, &bufferInfo);
- if (status != AMEDIA_OK) {
- LOG(ERROR) << "writeSampleData returned " << status;
- return status;
- }
-
- } while (sample->info.presentationTimeUs < segmentEndTimeUs && !track.mReachedEos);
+ // Exit if all tracks have reached end of stream.
+ if (outputTrackIter == mSortedTracks.end()) {
+ break;
}
- // TODO(lnilsson): Add option to toggle progress reporting on/off.
- if (primaryTrackIndex >= 0) {
- const TrackRecord& track = mTracks[primaryTrackIndex];
+ // Remove the track from the set, update it, and then reinsert it to keep the set in order.
+ TrackRecord* track = *outputTrackIter;
+ mSortedTracks.erase(outputTrackIter);
- const int64_t elapsed = track.mPrevSampleTimeUs - track.mFirstSampleTimeUs;
- int32_t progress = (elapsed * 100) / track.mDurationUs;
+ std::shared_ptr<MediaSample> sample;
+ if (track->mSampleQueue->dequeue(&sample)) {
+ // Track queue was aborted.
+ return AMEDIA_ERROR_UNKNOWN; // TODO(lnilsson): Custom error code.
+ } else if (sample->info.flags & SAMPLE_FLAG_END_OF_STREAM) {
+ // Track reached end of stream.
+ track->mReachedEos = true;
+
+ // Preserve source track duration by setting the appropriate timestamp on the
+ // empty End-Of-Stream sample.
+ if (track->mDurationUs > 0 && track->mFirstSampleTimeSet) {
+ sample->info.presentationTimeUs = track->mDurationUs + track->mFirstSampleTimeUs;
+ }
+ }
+
+ track->mPrevSampleTimeUs = sample->info.presentationTimeUs;
+ if (!track->mFirstSampleTimeSet) {
+ // Record the first sample's timestamp in order to translate duration to EOS
+ // time for tracks that does not start at 0.
+ track->mFirstSampleTimeUs = sample->info.presentationTimeUs;
+ track->mFirstSampleTimeSet = true;
+ }
+
+ bufferInfo.offset = sample->dataOffset;
+ bufferInfo.size = sample->info.size;
+ bufferInfo.flags = sample->info.flags;
+ bufferInfo.presentationTimeUs = sample->info.presentationTimeUs;
+
+ media_status_t status =
+ mMuxer->writeSampleData(track->mTrackIndex, sample->buffer, &bufferInfo);
+ if (status != AMEDIA_OK) {
+ LOG(ERROR) << "writeSampleData returned " << status;
+ return status;
+ }
+ sample.reset();
+
+ // TODO(lnilsson): Add option to toggle progress reporting on/off.
+ if (track->mTrackIndex == primaryTrackIndex) {
+ const int64_t elapsed = track->mPrevSampleTimeUs - track->mFirstSampleTimeUs;
+ int32_t progress = (elapsed * 100) / track->mDurationUs;
progress = std::clamp(progress, 0, 100);
if (progress > lastProgressUpdate) {
@@ -273,7 +297,9 @@
}
}
- segmentEndTimeUs += mTrackSegmentLengthUs;
+ if (!track->mReachedEos) {
+ mSortedTracks.insert(track);
+ }
}
return AMEDIA_OK;
diff --git a/media/libmediatranscoding/transcoder/NdkCommon.cpp b/media/libmediatranscoding/transcoder/NdkCommon.cpp
index 67a8e10..d8cf1c8 100644
--- a/media/libmediatranscoding/transcoder/NdkCommon.cpp
+++ b/media/libmediatranscoding/transcoder/NdkCommon.cpp
@@ -35,6 +35,7 @@
const char* AMEDIA_MIMETYPE_VIDEO_H263 = "video/3gpp";
/* TODO(b/153592281) */
+const char* TBD_AMEDIACODEC_PARAMETER_KEY_ALLOW_FRAME_DROP = "allow-frame-drop";
const char* TBD_AMEDIACODEC_PARAMETER_KEY_REQUEST_SYNC_FRAME = "request-sync";
const char* TBD_AMEDIACODEC_PARAMETER_KEY_VIDEO_BITRATE = "video-bitrate";
const char* TBD_AMEDIACODEC_PARAMETER_KEY_MAX_B_FRAMES = "max-bframes";
diff --git a/media/libmediatranscoding/transcoder/VideoTrackTranscoder.cpp b/media/libmediatranscoding/transcoder/VideoTrackTranscoder.cpp
index e192d2a..5702627 100644
--- a/media/libmediatranscoding/transcoder/VideoTrackTranscoder.cpp
+++ b/media/libmediatranscoding/transcoder/VideoTrackTranscoder.cpp
@@ -18,7 +18,9 @@
#define LOG_TAG "VideoTrackTranscoder"
#include <android-base/logging.h>
+#include <media/NdkCommon.h>
#include <media/VideoTrackTranscoder.h>
+#include <utils/AndroidThreads.h>
namespace android {
@@ -246,7 +248,17 @@
return AMEDIA_ERROR_UNSUPPORTED;
}
- status = AMediaCodec_configure(mDecoder, mSourceFormat.get(), mSurface, NULL /* crypto */,
+ auto decoderFormat = std::shared_ptr<AMediaFormat>(AMediaFormat_new(), &AMediaFormat_delete);
+ if (!decoderFormat ||
+ AMediaFormat_copy(decoderFormat.get(), mSourceFormat.get()) != AMEDIA_OK) {
+ LOG(ERROR) << "Unable to copy source format";
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+
+ // Prevent decoder from overwriting frames that the encoder has not yet consumed.
+ AMediaFormat_setInt32(decoderFormat.get(), TBD_AMEDIACODEC_PARAMETER_KEY_ALLOW_FRAME_DROP, 0);
+
+ status = AMediaCodec_configure(mDecoder, decoderFormat.get(), mSurface, NULL /* crypto */,
0 /* flags */);
if (status != AMEDIA_OK) {
LOG(ERROR) << "Unable to configure video decoder: " << status;
@@ -451,6 +463,8 @@
}
media_status_t VideoTrackTranscoder::runTranscodeLoop() {
+ androidSetThreadPriority(0 /* tid (0 = current) */, ANDROID_PRIORITY_VIDEO);
+
// Push start decoder and encoder as two messages, so that these are subject to the
// stop request as well. If the job is cancelled (or paused) immediately after start,
// we don't need to waste time start then stop the codecs.
diff --git a/media/libmediatranscoding/transcoder/benchmark/Android.bp b/media/libmediatranscoding/transcoder/benchmark/Android.bp
new file mode 100644
index 0000000..507c943
--- /dev/null
+++ b/media/libmediatranscoding/transcoder/benchmark/Android.bp
@@ -0,0 +1,6 @@
+cc_test {
+ name: "MediaTranscoderBenchmark",
+ srcs: ["MediaTranscoderBenchmark.cpp"],
+ shared_libs: ["libmediatranscoder", "libmediandk"],
+ static_libs: ["libgoogle-benchmark"],
+}
diff --git a/media/libmediatranscoding/transcoder/benchmark/MediaTranscoderBenchmark.cpp b/media/libmediatranscoding/transcoder/benchmark/MediaTranscoderBenchmark.cpp
new file mode 100644
index 0000000..b31b675
--- /dev/null
+++ b/media/libmediatranscoding/transcoder/benchmark/MediaTranscoderBenchmark.cpp
@@ -0,0 +1,205 @@
+/*
+ * 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.
+ */
+
+/**
+ * Native media transcoder library benchmark tests.
+ *
+ * How to run the benchmark:
+ *
+ * 1. Download the media assets from http://go/transcodingbenchmark and push the directory
+ * ("TranscodingBenchmark") to /data/local/tmp.
+ *
+ * 2. Compile the benchmark and sync to device:
+ * $ mm -j72 && adb sync
+ *
+ * 3. Run:
+ * $ adb shell /data/nativetest64/MediaTranscoderBenchmark/MediaTranscoderBenchmark
+ */
+
+#include <benchmark/benchmark.h>
+#include <fcntl.h>
+#include <media/MediaTranscoder.h>
+
+using namespace android;
+
+class TranscoderCallbacks : public MediaTranscoder::CallbackInterface {
+public:
+ virtual void onFinished(const MediaTranscoder* transcoder __unused) override {
+ std::unique_lock<std::mutex> lock(mMutex);
+ mFinished = true;
+ mCondition.notify_all();
+ }
+
+ virtual void onError(const MediaTranscoder* transcoder __unused,
+ media_status_t error) override {
+ std::unique_lock<std::mutex> lock(mMutex);
+ 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 Parcel>& pausedState
+ __unused) override {}
+
+ bool waitForTranscodingFinished() {
+ std::unique_lock<std::mutex> lock(mMutex);
+ while (!mFinished) {
+ if (mCondition.wait_for(lock, std::chrono::minutes(5)) == std::cv_status::timeout) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ media_status_t mStatus = AMEDIA_OK;
+
+private:
+ std::mutex mMutex;
+ std::condition_variable mCondition;
+ bool mFinished = false;
+};
+
+static void TranscodeMediaFile(benchmark::State& state, const std::string& srcFileName,
+ const std::string& dstFileName, bool includeAudio) {
+ // Default bitrate
+ static constexpr int32_t kVideoBitRate = 20 * 1000 * 1000; // 20Mbs
+ // Write-only, create file if non-existent.
+ static constexpr int kDstOpenFlags = O_WRONLY | O_CREAT;
+ // User R+W permission.
+ static constexpr int kDstFileMode = S_IRUSR | S_IWUSR;
+ // Asset directory
+ static const std::string kAssetDirectory = "/data/local/tmp/TranscodingBenchmark/";
+
+ int srcFd = 0;
+ int dstFd = 0;
+
+ std::string srcPath = kAssetDirectory + srcFileName;
+ std::string dstPath = kAssetDirectory + dstFileName;
+
+ auto callbacks = std::make_shared<TranscoderCallbacks>();
+ media_status_t status = AMEDIA_OK;
+
+ if ((srcFd = open(srcPath.c_str(), O_RDONLY)) < 0) {
+ state.SkipWithError("Unable to open source file");
+ goto exit;
+ }
+ if ((dstFd = open(dstPath.c_str(), kDstOpenFlags, kDstFileMode)) < 0) {
+ state.SkipWithError("Unable to open destination file");
+ goto exit;
+ }
+
+ for (auto _ : state) {
+ auto transcoder = MediaTranscoder::create(callbacks, nullptr);
+
+ status = transcoder->configureSource(srcFd);
+ if (status != AMEDIA_OK) {
+ state.SkipWithError("Unable to configure transcoder source");
+ goto exit;
+ }
+
+ status = transcoder->configureDestination(dstFd);
+ if (status != AMEDIA_OK) {
+ state.SkipWithError("Unable to configure transcoder destination");
+ goto exit;
+ }
+
+ std::vector<std::shared_ptr<AMediaFormat>> trackFormats = transcoder->getTrackFormats();
+ for (int i = 0; i < trackFormats.size(); ++i) {
+ AMediaFormat* srcFormat = trackFormats[i].get();
+ AMediaFormat* dstFormat = nullptr;
+
+ const char* mime = nullptr;
+ if (!AMediaFormat_getString(srcFormat, AMEDIAFORMAT_KEY_MIME, &mime)) {
+ state.SkipWithError("Source track format does not have MIME type");
+ goto exit;
+ }
+
+ if (strncmp(mime, "video/", 6) == 0) {
+ dstFormat = AMediaFormat_new();
+ AMediaFormat_setInt32(dstFormat, AMEDIAFORMAT_KEY_BIT_RATE, kVideoBitRate);
+
+ int32_t frameCount;
+ if (AMediaFormat_getInt32(srcFormat, AMEDIAFORMAT_KEY_FRAME_COUNT, &frameCount)) {
+ state.counters["VideoFrameRate"] =
+ benchmark::Counter(frameCount, benchmark::Counter::kIsRate);
+ }
+ } else if (!includeAudio && strncmp(mime, "audio/", 6) == 0) {
+ continue;
+ }
+
+ status = transcoder->configureTrackFormat(i, dstFormat);
+ if (dstFormat != nullptr) {
+ AMediaFormat_delete(dstFormat);
+ }
+ if (status != AMEDIA_OK) {
+ state.SkipWithError("Unable to configure track");
+ goto exit;
+ }
+ }
+
+ status = transcoder->start();
+ if (status != AMEDIA_OK) {
+ state.SkipWithError("Unable to start transcoder");
+ goto exit;
+ }
+
+ if (!callbacks->waitForTranscodingFinished()) {
+ transcoder->cancel();
+ state.SkipWithError("Transcoder timed out");
+ goto exit;
+ }
+ if (callbacks->mStatus != AMEDIA_OK) {
+ state.SkipWithError("Transcoder error when running");
+ goto exit;
+ }
+ }
+
+exit:
+ if (srcFd > 0) close(srcFd);
+ if (dstFd > 0) close(dstFd);
+}
+
+// Benchmark registration wrapper for transcoding.
+#define TRANSCODER_BENCHMARK(func) \
+ BENCHMARK(func)->UseRealTime()->MeasureProcessCPUTime()->Unit(benchmark::kMillisecond)
+
+static void BM_TranscodeAvc2AvcAudioVideo2AudioVideo(benchmark::State& state) {
+ TranscodeMediaFile(state, "video_1920x1080_3648frame_h264_22Mbps_30fps_aac.mp4",
+ "video_1920x1080_3648frame_h264_22Mbps_30fps_aac_transcoded_AV.mp4",
+ true /* includeAudio */);
+}
+
+static void BM_TranscodeAvc2AvcAudioVideo2Video(benchmark::State& state) {
+ TranscodeMediaFile(state, "video_1920x1080_3648frame_h264_22Mbps_30fps_aac.mp4",
+ "video_1920x1080_3648frame_h264_22Mbps_30fps_aac_transcoded_V.mp4",
+ false /* includeAudio */);
+}
+
+static void BM_TranscodeAvc2AvcVideo2Video(benchmark::State& state) {
+ TranscodeMediaFile(state, "video_1920x1080_3648frame_h264_22Mbps_30fps.mp4",
+ "video_1920x1080_3648frame_h264_22Mbps_30fps_transcoded_V.mp4",
+ false /* includeAudio */);
+}
+
+TRANSCODER_BENCHMARK(BM_TranscodeAvc2AvcAudioVideo2AudioVideo);
+TRANSCODER_BENCHMARK(BM_TranscodeAvc2AvcAudioVideo2Video);
+TRANSCODER_BENCHMARK(BM_TranscodeAvc2AvcVideo2Video);
+
+BENCHMARK_MAIN();
diff --git a/media/libmediatranscoding/transcoder/include/media/MediaSampleQueue.h b/media/libmediatranscoding/transcoder/include/media/MediaSampleQueue.h
index dc22423..c6cf1a4 100644
--- a/media/libmediatranscoding/transcoder/include/media/MediaSampleQueue.h
+++ b/media/libmediatranscoding/transcoder/include/media/MediaSampleQueue.h
@@ -50,6 +50,12 @@
bool dequeue(std::shared_ptr<MediaSample>* sample /* nonnull */);
/**
+ * Checks if the queue currently holds any media samples.
+ * @return True if the queue is empty or has been aborted. False otherwise.
+ */
+ bool isEmpty();
+
+ /**
* Aborts the queue operation. This clears the queue and notifies waiting consumers. After the
* has been aborted it is not possible to enqueue more samples, and dequeue will return null.
*/
diff --git a/media/libmediatranscoding/transcoder/include/media/MediaSampleWriter.h b/media/libmediatranscoding/transcoder/include/media/MediaSampleWriter.h
index 92ddc2f..d4b1fcf 100644
--- a/media/libmediatranscoding/transcoder/include/media/MediaSampleWriter.h
+++ b/media/libmediatranscoding/transcoder/include/media/MediaSampleWriter.h
@@ -26,6 +26,7 @@
#include <functional>
#include <memory>
#include <mutex>
+#include <set>
#include <thread>
namespace android {
@@ -61,15 +62,18 @@
};
/**
- * MediaSampleWriter writes samples in interleaved segments of a configurable duration.
- * Each track have its own MediaSampleQueue from which samples are dequeued by the sample writer in
- * output order. The dequeued samples are written to an instance of the writer's muxer interface.
- * The default muxer interface implementation is based directly on AMediaMuxer.
+ * MediaSampleWriter writes samples to a muxer while keeping its input sources synchronized. Each
+ * source track have its own MediaSampleQueue from which samples are dequeued by the sample writer
+ * and written to the muxer. The sample writer always prioritizes dequeueing samples from the source
+ * track that is farthest behind by comparing sample timestamps. If the slowest track does not have
+ * any samples pending the writer moves on to the next track but never allows tracks to diverge more
+ * than a configurable duration of time. The default muxer interface implementation is based
+ * directly on AMediaMuxer.
*/
class MediaSampleWriter {
public:
- /** The default segment length. */
- static constexpr uint32_t kDefaultTrackSegmentLengthUs = 1 * 1000 * 1000; // 1 sec.
+ /** The default maximum track divergence in microseconds. */
+ static constexpr uint32_t kDefaultMaxTrackDivergenceUs = 1 * 1000 * 1000; // 1 second.
/** Callback interface. */
class CallbackInterface {
@@ -87,14 +91,14 @@
};
/**
- * Constructor with custom segment length.
- * @param trackSegmentLengthUs The segment length to use for this MediaSampleWriter.
+ * Constructor with custom maximum track divergence.
+ * @param maxTrackDivergenceUs The maximum track divergence in microseconds.
*/
- MediaSampleWriter(uint32_t trackSegmentLengthUs)
- : mTrackSegmentLengthUs(trackSegmentLengthUs), mMuxer(nullptr), mState(UNINITIALIZED){};
+ MediaSampleWriter(uint32_t maxTrackDivergenceUs)
+ : mMaxTrackDivergenceUs(maxTrackDivergenceUs), mMuxer(nullptr), mState(UNINITIALIZED){};
- /** Constructor using the default segment length. */
- MediaSampleWriter() : MediaSampleWriter(kDefaultTrackSegmentLengthUs){};
+ /** Constructor using the default maximum track divergence. */
+ MediaSampleWriter() : MediaSampleWriter(kDefaultMaxTrackDivergenceUs){};
/** Destructor. */
~MediaSampleWriter();
@@ -147,20 +151,16 @@
bool stop();
private:
- media_status_t writeSamples();
- media_status_t runWriterLoop();
-
struct TrackRecord {
TrackRecord(const std::shared_ptr<MediaSampleQueue>& sampleQueue, size_t trackIndex,
- int64_t durationUs, bool isVideo)
+ int64_t durationUs)
: mSampleQueue(sampleQueue),
mTrackIndex(trackIndex),
mDurationUs(durationUs),
mFirstSampleTimeUs(0),
- mPrevSampleTimeUs(0),
+ mPrevSampleTimeUs(INT64_MIN),
mFirstSampleTimeSet(false),
- mReachedEos(false),
- mIsVideo(isVideo) {}
+ mReachedEos(false) {}
std::shared_ptr<MediaSampleQueue> mSampleQueue;
const size_t mTrackIndex;
@@ -169,13 +169,19 @@
int64_t mPrevSampleTimeUs;
bool mFirstSampleTimeSet;
bool mReachedEos;
- bool mIsVideo;
+
+ struct compare {
+ bool operator()(const TrackRecord* lhs, const TrackRecord* rhs) const {
+ return lhs->mPrevSampleTimeUs < rhs->mPrevSampleTimeUs;
+ }
+ };
};
- const uint32_t mTrackSegmentLengthUs;
+ const uint32_t mMaxTrackDivergenceUs;
std::weak_ptr<CallbackInterface> mCallbacks;
std::shared_ptr<MediaSampleWriterMuxerInterface> mMuxer;
- std::vector<TrackRecord> mTracks;
+ std::vector<std::unique_ptr<TrackRecord>> mAllTracks;
+ std::multiset<TrackRecord*, TrackRecord::compare> mSortedTracks;
std::thread mThread;
std::mutex mStateMutex;
@@ -185,6 +191,10 @@
STARTED,
STOPPED,
} mState GUARDED_BY(mStateMutex);
+
+ media_status_t writeSamples();
+ media_status_t runWriterLoop();
+ std::multiset<TrackRecord*>::iterator getNextOutputTrack();
};
} // namespace android
diff --git a/media/libmediatranscoding/transcoder/include/media/MediaTranscoder.h b/media/libmediatranscoding/transcoder/include/media/MediaTranscoder.h
index 031d01e..8d96867 100644
--- a/media/libmediatranscoding/transcoder/include/media/MediaTranscoder.h
+++ b/media/libmediatranscoding/transcoder/include/media/MediaTranscoder.h
@@ -17,8 +17,6 @@
#ifndef ANDROID_MEDIA_TRANSCODER_H
#define ANDROID_MEDIA_TRANSCODER_H
-#include <binder/Parcel.h>
-#include <binder/Parcelable.h>
#include <media/MediaSampleWriter.h>
#include <media/MediaTrackTranscoderCallback.h>
#include <media/NdkMediaError.h>
diff --git a/media/libmediatranscoding/transcoder/include/media/NdkCommon.h b/media/libmediatranscoding/transcoder/include/media/NdkCommon.h
index dcba812..044d024 100644
--- a/media/libmediatranscoding/transcoder/include/media/NdkCommon.h
+++ b/media/libmediatranscoding/transcoder/include/media/NdkCommon.h
@@ -46,6 +46,7 @@
static constexpr int COLOR_FormatSurface = 0x7f000789;
// constants not defined in NDK
+extern const char* TBD_AMEDIACODEC_PARAMETER_KEY_ALLOW_FRAME_DROP;
extern const char* TBD_AMEDIACODEC_PARAMETER_KEY_REQUEST_SYNC_FRAME;
extern const char* TBD_AMEDIACODEC_PARAMETER_KEY_VIDEO_BITRATE;
extern const char* TBD_AMEDIACODEC_PARAMETER_KEY_MAX_B_FRAMES;
diff --git a/media/libmediatranscoding/transcoder/tests/MediaSampleQueueTests.cpp b/media/libmediatranscoding/transcoder/tests/MediaSampleQueueTests.cpp
index 2046ca0..6357e4d 100644
--- a/media/libmediatranscoding/transcoder/tests/MediaSampleQueueTests.cpp
+++ b/media/libmediatranscoding/transcoder/tests/MediaSampleQueueTests.cpp
@@ -46,10 +46,12 @@
static constexpr int kNumSamples = 4;
MediaSampleQueue sampleQueue;
+ EXPECT_TRUE(sampleQueue.isEmpty());
// Enqueue loop.
for (int i = 0; i < kNumSamples; ++i) {
sampleQueue.enqueue(newSample(i));
+ EXPECT_FALSE(sampleQueue.isEmpty());
}
// Dequeue loop.
@@ -60,6 +62,7 @@
EXPECT_EQ(sample->bufferId, i);
EXPECT_FALSE(aborted);
}
+ EXPECT_TRUE(sampleQueue.isEmpty());
}
TEST_F(MediaSampleQueueTests, TestInterleavedDequeueOrder) {
@@ -71,12 +74,14 @@
// Enqueue and dequeue.
for (int i = 0; i < kNumSamples; ++i) {
sampleQueue.enqueue(newSample(i));
+ EXPECT_FALSE(sampleQueue.isEmpty());
std::shared_ptr<MediaSample> sample;
bool aborted = sampleQueue.dequeue(&sample);
EXPECT_NE(sample, nullptr);
EXPECT_EQ(sample->bufferId, i);
EXPECT_FALSE(aborted);
+ EXPECT_TRUE(sampleQueue.isEmpty());
}
}
@@ -98,6 +103,7 @@
EXPECT_NE(sample, nullptr);
EXPECT_EQ(sample->bufferId, 1);
EXPECT_FALSE(aborted);
+ EXPECT_TRUE(sampleQueue.isEmpty());
enqueueThread.join();
}
@@ -160,7 +166,9 @@
EXPECT_FALSE(bufferReleased[i]);
}
+ EXPECT_FALSE(sampleQueue.isEmpty());
sampleQueue.abort();
+ EXPECT_TRUE(sampleQueue.isEmpty());
for (int i = 0; i < kNumSamples; ++i) {
EXPECT_TRUE(bufferReleased[i]);
diff --git a/media/libmediatranscoding/transcoder/tests/MediaSampleWriterTests.cpp b/media/libmediatranscoding/transcoder/tests/MediaSampleWriterTests.cpp
index c82ec28..64240d4 100644
--- a/media/libmediatranscoding/transcoder/tests/MediaSampleWriterTests.cpp
+++ b/media/libmediatranscoding/transcoder/tests/MediaSampleWriterTests.cpp
@@ -78,7 +78,22 @@
return {.type = Event::WriteSample, .trackIndex = trackIndex, .data = data, .info = *info};
}
- const Event& popEvent() {
+ static Event WriteSampleWithPts(size_t trackIndex, int64_t pts) {
+ return {.type = Event::WriteSample, .trackIndex = trackIndex, .info = {0, 0, pts, 0}};
+ }
+
+ void pushEvent(const Event& e) {
+ std::unique_lock<std::mutex> lock(mMutex);
+ mEventQueue.push_back(e);
+ mCondition.notify_one();
+ }
+
+ const Event& popEvent(bool wait = false) {
+ std::unique_lock<std::mutex> lock(mMutex);
+ while (wait && mEventQueue.empty()) {
+ mCondition.wait_for(lock, std::chrono::milliseconds(200));
+ }
+
if (mEventQueue.empty()) {
mPoppedEvent = NoEvent;
} else {
@@ -92,6 +107,8 @@
Event mPoppedEvent;
std::list<Event> mEventQueue;
ssize_t mTrackCount = 0;
+ std::mutex mMutex;
+ std::condition_variable mCondition;
};
bool operator==(const AMediaCodecBufferInfo& lhs, const AMediaCodecBufferInfo& rhs) {
@@ -245,11 +262,15 @@
static std::shared_ptr<MediaSample> newSampleWithPts(int64_t ptsUs) {
static uint32_t sampleCount = 0;
- // Use sampleCount to get a unique dummy sample.
+ // Use sampleCount to get a unique mock sample.
uint32_t sampleId = ++sampleCount;
return newSample(ptsUs, 0, sampleId, sampleId, reinterpret_cast<const uint8_t*>(sampleId));
}
+ static std::shared_ptr<MediaSample> newSampleWithPtsOnly(int64_t ptsUs) {
+ return newSample(ptsUs, 0, 0, 0, nullptr);
+ }
+
void SetUp() override {
LOG(DEBUG) << "MediaSampleWriterTests set up";
mTestMuxer = std::make_shared<TestMuxer>();
@@ -345,10 +366,9 @@
}
TEST_F(MediaSampleWriterTests, TestProgressUpdate) {
- static constexpr uint32_t kSegmentLengthUs = 1;
const TestMediaSource& mediaSource = getMediaSource();
- MediaSampleWriter writer{kSegmentLengthUs};
+ MediaSampleWriter writer{};
EXPECT_TRUE(writer.init(mTestMuxer, mTestCallbacks));
std::shared_ptr<AMediaFormat> videoFormat =
@@ -370,9 +390,7 @@
}
TEST_F(MediaSampleWriterTests, TestInterleaving) {
- static constexpr uint32_t kSegmentLength = MediaSampleWriter::kDefaultTrackSegmentLengthUs;
-
- MediaSampleWriter writer{kSegmentLength};
+ MediaSampleWriter writer{};
EXPECT_TRUE(writer.init(mTestMuxer, mTestCallbacks));
// Use two tracks for this test.
@@ -398,18 +416,19 @@
};
addSampleToTrackWithPts(0, 0);
- addSampleToTrackWithPts(0, kSegmentLength / 2);
- addSampleToTrackWithPts(0, kSegmentLength); // Track 0 reached 1st segment end
+ addSampleToTrackWithPts(1, 4);
- addSampleToTrackWithPts(1, 0);
- addSampleToTrackWithPts(1, kSegmentLength); // Track 1 reached 1st segment end
+ addSampleToTrackWithPts(0, 1);
+ addSampleToTrackWithPts(0, 2);
+ addSampleToTrackWithPts(0, 3);
+ addSampleToTrackWithPts(0, 10);
- addSampleToTrackWithPts(0, kSegmentLength * 2); // Track 0 reached 2nd segment end
+ addSampleToTrackWithPts(1, 5);
+ addSampleToTrackWithPts(1, 6);
+ addSampleToTrackWithPts(1, 11);
- addSampleToTrackWithPts(1, kSegmentLength + 1);
- addSampleToTrackWithPts(1, kSegmentLength * 2); // Track 1 reached 2nd segment end
-
- addSampleToTrackWithPts(0, kSegmentLength * 2 + 1);
+ addSampleToTrackWithPts(0, 12);
+ addSampleToTrackWithPts(1, 13);
for (int trackIndex = 0; trackIndex < kNumTracks; ++trackIndex) {
sampleQueues[trackIndex]->enqueue(newSampleEos());
@@ -443,7 +462,10 @@
int64_t duration = 0;
AMediaFormat_getInt64(trackFormat.get(), AMEDIAFORMAT_KEY_DURATION, &duration);
- const AMediaCodecBufferInfo info = {0, 0, duration, AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM};
+ // EOS timestamp = first sample timestamp + duration.
+ const int64_t endTime = duration + (trackIndex == 1 ? 4 : 0);
+ const AMediaCodecBufferInfo info = {0, 0, endTime, AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM};
+
EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::WriteSample(trackIndex, nullptr, &info));
}
@@ -452,6 +474,124 @@
EXPECT_TRUE(mTestCallbacks->hasFinished());
}
+TEST_F(MediaSampleWriterTests, TestMaxDivergence) {
+ static constexpr uint32_t kMaxDivergenceUs = 10;
+
+ MediaSampleWriter writer{kMaxDivergenceUs};
+ EXPECT_TRUE(writer.init(mTestMuxer, mTestCallbacks));
+
+ // Use two tracks for this test.
+ static constexpr int kNumTracks = 2;
+ std::shared_ptr<MediaSampleQueue> sampleQueues[kNumTracks];
+ const TestMediaSource& mediaSource = getMediaSource();
+
+ for (int trackIdx = 0; trackIdx < kNumTracks; ++trackIdx) {
+ sampleQueues[trackIdx] = std::make_shared<MediaSampleQueue>();
+
+ auto trackFormat = mediaSource.mTrackFormats[trackIdx % mediaSource.mTrackCount];
+ EXPECT_TRUE(writer.addTrack(sampleQueues[trackIdx], trackFormat));
+ EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::AddTrack(trackFormat.get()));
+ }
+
+ ASSERT_TRUE(writer.start());
+ EXPECT_EQ(mTestMuxer->popEvent(true), TestMuxer::Start());
+
+ // The first samples of each track can be written in any order since the writer does not have
+ // any previous timestamps to compare.
+ sampleQueues[0]->enqueue(newSampleWithPtsOnly(0));
+ sampleQueues[1]->enqueue(newSampleWithPtsOnly(1));
+ mTestMuxer->popEvent(true);
+ mTestMuxer->popEvent(true);
+
+ // The writer will now be waiting on track 0 since it has the lowest previous timestamp.
+ sampleQueues[0]->enqueue(newSampleWithPtsOnly(kMaxDivergenceUs + 1));
+ sampleQueues[0]->enqueue(newSampleWithPtsOnly(kMaxDivergenceUs + 2));
+
+ // The writer should dequeue the first sample above but not the second since track 0 now is too
+ // far ahead. Instead it should wait for track 1.
+ EXPECT_EQ(mTestMuxer->popEvent(true), TestMuxer::WriteSampleWithPts(0, kMaxDivergenceUs + 1));
+
+ // Enqueue a sample from track 1 that puts it within acceptable divergence range again. The
+ // writer should dequeue that sample and then go back to track 0 since track 1 is empty.
+ sampleQueues[1]->enqueue(newSampleWithPtsOnly(kMaxDivergenceUs));
+ EXPECT_EQ(mTestMuxer->popEvent(true), TestMuxer::WriteSampleWithPts(1, kMaxDivergenceUs));
+ EXPECT_EQ(mTestMuxer->popEvent(true), TestMuxer::WriteSampleWithPts(0, kMaxDivergenceUs + 2));
+
+ // Both tracks are now empty so the writer should wait for track 1 which is farthest behind.
+ sampleQueues[1]->enqueue(newSampleWithPtsOnly(kMaxDivergenceUs + 3));
+ EXPECT_EQ(mTestMuxer->popEvent(true), TestMuxer::WriteSampleWithPts(1, kMaxDivergenceUs + 3));
+
+ for (int trackIndex = 0; trackIndex < kNumTracks; ++trackIndex) {
+ sampleQueues[trackIndex]->enqueue(newSampleEos());
+ }
+
+ // Wait for writer to complete.
+ mTestCallbacks->waitForWritingFinished();
+
+ // Verify EOS samples.
+ for (int trackIndex = 0; trackIndex < kNumTracks; ++trackIndex) {
+ auto trackFormat = mediaSource.mTrackFormats[trackIndex % mediaSource.mTrackCount];
+ int64_t duration = 0;
+ AMediaFormat_getInt64(trackFormat.get(), AMEDIAFORMAT_KEY_DURATION, &duration);
+
+ // EOS timestamp = first sample timestamp + duration.
+ const int64_t endTime = duration + (trackIndex == 1 ? 1 : 0);
+ const AMediaCodecBufferInfo info = {0, 0, endTime, AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM};
+ EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::WriteSample(trackIndex, nullptr, &info));
+ }
+
+ EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::Stop());
+ EXPECT_TRUE(writer.stop());
+ EXPECT_TRUE(mTestCallbacks->hasFinished());
+}
+
+TEST_F(MediaSampleWriterTests, TestTimestampDivergenceOverflow) {
+ auto testCallbacks = std::make_shared<TestCallbacks>(false /* expectSuccess */);
+ MediaSampleWriter writer{};
+ EXPECT_TRUE(writer.init(mTestMuxer, testCallbacks));
+
+ // Use two tracks for this test.
+ static constexpr int kNumTracks = 2;
+ std::shared_ptr<MediaSampleQueue> sampleQueues[kNumTracks];
+ const TestMediaSource& mediaSource = getMediaSource();
+
+ for (int trackIdx = 0; trackIdx < kNumTracks; ++trackIdx) {
+ sampleQueues[trackIdx] = std::make_shared<MediaSampleQueue>();
+
+ auto trackFormat = mediaSource.mTrackFormats[trackIdx % mediaSource.mTrackCount];
+ EXPECT_TRUE(writer.addTrack(sampleQueues[trackIdx], trackFormat));
+ EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::AddTrack(trackFormat.get()));
+ }
+
+ // Prime track 0 with lower end of INT64 range, and track 1 with positive timestamps making the
+ // difference larger than INT64_MAX.
+ sampleQueues[0]->enqueue(newSampleWithPtsOnly(INT64_MIN + 1));
+ sampleQueues[1]->enqueue(newSampleWithPtsOnly(1000));
+ sampleQueues[1]->enqueue(newSampleWithPtsOnly(1001));
+
+ ASSERT_TRUE(writer.start());
+ EXPECT_EQ(mTestMuxer->popEvent(true), TestMuxer::Start());
+
+ // The first sample of each track can be pulled in any order.
+ mTestMuxer->popEvent(true);
+ mTestMuxer->popEvent(true);
+
+ // Wait to make sure the writer compares track 0 empty against track 1 non-empty. The writer
+ // should handle the large timestamp differences and chose to wait for track 0 even though
+ // track 1 has a sample ready.
+ std::this_thread::sleep_for(std::chrono::milliseconds(20));
+
+ sampleQueues[0]->enqueue(newSampleWithPtsOnly(INT64_MIN + 2));
+ sampleQueues[0]->enqueue(newSampleWithPtsOnly(1000)); // <-- Close the gap between the tracks.
+ EXPECT_EQ(mTestMuxer->popEvent(true), TestMuxer::WriteSampleWithPts(0, INT64_MIN + 2));
+ EXPECT_EQ(mTestMuxer->popEvent(true), TestMuxer::WriteSampleWithPts(0, 1000));
+ EXPECT_EQ(mTestMuxer->popEvent(true), TestMuxer::WriteSampleWithPts(1, 1001));
+
+ EXPECT_TRUE(writer.stop());
+ EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::Stop());
+ EXPECT_TRUE(testCallbacks->hasFinished());
+}
+
TEST_F(MediaSampleWriterTests, TestAbortInputQueue) {
MediaSampleWriter writer{};
std::shared_ptr<TestCallbacks> callbacks =
diff --git a/media/libmediatranscoding/transcoder/tests/VideoTrackTranscoderTests.cpp b/media/libmediatranscoding/transcoder/tests/VideoTrackTranscoderTests.cpp
index 4eecd90..b432553 100644
--- a/media/libmediatranscoding/transcoder/tests/VideoTrackTranscoderTests.cpp
+++ b/media/libmediatranscoding/transcoder/tests/VideoTrackTranscoderTests.cpp
@@ -92,8 +92,8 @@
std::shared_ptr<AMediaFormat> mDestinationFormat;
};
-TEST_F(VideoTrackTranscoderTests, SampleSanity) {
- LOG(DEBUG) << "Testing SampleSanity";
+TEST_F(VideoTrackTranscoderTests, SampleSoundness) {
+ LOG(DEBUG) << "Testing SampleSoundness";
std::shared_ptr<TestCallback> callback = std::make_shared<TestCallback>();
auto transcoder = VideoTrackTranscoder::create(callback);
diff --git a/media/libnbaio/include/media/nbaio/Pipe.h b/media/libnbaio/include/media/nbaio/Pipe.h
index 0431976..54dc08f 100644
--- a/media/libnbaio/include/media/nbaio/Pipe.h
+++ b/media/libnbaio/include/media/nbaio/Pipe.h
@@ -23,7 +23,7 @@
namespace android {
// Pipe is multi-thread safe for readers (see PipeReader), but safe for only a single writer thread.
-// It cannot UNDERRUN on write, unless we allow designation of a master reader that provides the
+// It cannot UNDERRUN on write, unless we allow designation of a primary reader that provides the
// time-base. Readers can be added and removed dynamically, and it's OK to have no readers.
class Pipe : public NBAIO_Sink {
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 6941198..382491e 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -888,7 +888,7 @@
sp<DataConverter> converter = mConverter[portIndex];
if (converter != NULL) {
- // here we assume sane conversions of max 4:1, so result fits in int32
+ // here we assume conversions of max 4:1, so result fits in int32
if (portIndex == kPortIndexInput) {
conversionBufferSize = converter->sourceSize(bufSize);
} else {
@@ -2609,15 +2609,15 @@
unsigned int numLayers = 0;
unsigned int numBLayers = 0;
int tags;
- char dummy;
+ char tmp;
OMX_VIDEO_ANDROID_TEMPORALLAYERINGPATTERNTYPE pattern =
OMX_VIDEO_AndroidTemporalLayeringPatternNone;
- if (sscanf(tsSchema.c_str(), "webrtc.vp8.%u-layer%c", &numLayers, &dummy) == 1
+ if (sscanf(tsSchema.c_str(), "webrtc.vp8.%u-layer%c", &numLayers, &tmp) == 1
&& numLayers > 0) {
pattern = OMX_VIDEO_AndroidTemporalLayeringPatternWebRTC;
} else if ((tags = sscanf(tsSchema.c_str(), "android.generic.%u%c%u%c",
- &numLayers, &dummy, &numBLayers, &dummy))
- && (tags == 1 || (tags == 3 && dummy == '+'))
+ &numLayers, &tmp, &numBLayers, &tmp))
+ && (tags == 1 || (tags == 3 && tmp == '+'))
&& numLayers > 0 && numLayers < UINT32_MAX - numBLayers) {
numLayers += numBLayers;
pattern = OMX_VIDEO_AndroidTemporalLayeringPatternAndroid;
@@ -4787,15 +4787,15 @@
unsigned int numLayers = 0;
unsigned int numBLayers = 0;
int tags;
- char dummy;
- if (sscanf(tsSchema.c_str(), "webrtc.vp8.%u-layer%c", &numLayers, &dummy) == 1
+ char tmp;
+ if (sscanf(tsSchema.c_str(), "webrtc.vp8.%u-layer%c", &numLayers, &tmp) == 1
&& numLayers > 0) {
pattern = OMX_VIDEO_VPXTemporalLayerPatternWebRTC;
tsType = OMX_VIDEO_AndroidTemporalLayeringPatternWebRTC;
tsLayers = numLayers;
} else if ((tags = sscanf(tsSchema.c_str(), "android.generic.%u%c%u%c",
- &numLayers, &dummy, &numBLayers, &dummy))
- && (tags == 1 || (tags == 3 && dummy == '+'))
+ &numLayers, &tmp, &numBLayers, &tmp))
+ && (tags == 1 || (tags == 3 && tmp == '+'))
&& numLayers > 0 && numLayers < UINT32_MAX - numBLayers) {
pattern = OMX_VIDEO_VPXTemporalLayerPatternWebRTC;
// VPX does not have a concept of B-frames, so just count all layers
@@ -7674,8 +7674,8 @@
mInputFormat->setInt64("android._stop-time-offset-us", stopTimeOffsetUs);
}
- int32_t dummy;
- if (params->findInt32("request-sync", &dummy)) {
+ int32_t tmp;
+ if (params->findInt32("request-sync", &tmp)) {
status_t err = requestIDRFrame();
if (err != OK) {
diff --git a/media/libstagefright/FrameCaptureProcessor.cpp b/media/libstagefright/FrameCaptureProcessor.cpp
index 96c1195..ee642d4 100644
--- a/media/libstagefright/FrameCaptureProcessor.cpp
+++ b/media/libstagefright/FrameCaptureProcessor.cpp
@@ -164,14 +164,21 @@
if (err != OK) {
ALOGE("drawLayers returned err %d", err);
- return err;
+ } else {
+ err = fence->wait(500);
+ if (err != OK) {
+ ALOGW("wait for fence returned err %d", err);
+ err = OK;
+ }
}
-
- err = fence->wait(500);
- if (err != OK) {
- ALOGW("wait for fence returned err %d", err);
+ mRE->cleanupPostRender();
+ // Unbind the buffer now to remove it from the RenderEngine's image cache.
+ // The buffer was put into the image cache during the drawLayers() call above.
+ const sp<GraphicBuffer> &gbuf = layerSettings.source.buffer.buffer;
+ if (gbuf != nullptr) {
+ mRE->unbindExternalTextureBuffer(gbuf->getId());
}
- return OK;
+ return err;
}
void FrameCaptureProcessor::onMessageReceived(const sp<AMessage> &msg) {
diff --git a/media/libstagefright/codecs/aacdec/DrcPresModeWrap.cpp b/media/libstagefright/codecs/aacdec/DrcPresModeWrap.cpp
index 168d140..157cab6 100644
--- a/media/libstagefright/codecs/aacdec/DrcPresModeWrap.cpp
+++ b/media/libstagefright/codecs/aacdec/DrcPresModeWrap.cpp
@@ -217,7 +217,7 @@
}
else { // handle other used encoder target levels
- // Sanity check: DRC presentation mode is only specified for max. 5.1 channels
+ // Validation check: DRC presentation mode is only specified for max. 5.1 channels
if (mStreamNrAACChan > 6) {
drcPresMode = 0;
}
@@ -308,7 +308,7 @@
} // switch()
} // if (mEncoderTarget == GPM_ENCODER_TARGET_LEVEL)
- // sanity again
+ // validation check again
if (newHeavy == 1) {
newBoostFactor=127; // not really needed as the same would be done by the decoder anyway
newAttFactor = 127;
diff --git a/media/libstagefright/codecs/amrnb/enc/src/qgain475.cpp b/media/libstagefright/codecs/amrnb/enc/src/qgain475.cpp
index f8da589..08a5c15 100644
--- a/media/libstagefright/codecs/amrnb/enc/src/qgain475.cpp
+++ b/media/libstagefright/codecs/amrnb/enc/src/qgain475.cpp
@@ -1106,7 +1106,7 @@
// the real, quantized gains)
gc_pred(pred_st, MR475, sf1_code_nosharp,
&sf1_exp_gcode0, &sf1_frac_gcode0,
- &sf0_exp_gcode0, &sf0_gcode0); // last two args are dummy
+ &sf0_exp_gcode0, &sf0_gcode0); // last two args are unused
sf1_gcode0 = extract_l(Pow2(14, sf1_frac_gcode0));
tmp = add (tmp, 2);
@@ -1426,7 +1426,7 @@
the real, quantized gains) */
gc_pred(pred_st, MR475, sf1_code_nosharp,
&sf1_exp_gcode0, &sf1_frac_gcode0,
- &sf0_exp_gcode0, &sf0_gcode0, /* dummy args */
+ &sf0_exp_gcode0, &sf0_gcode0, /* unused args */
pOverflow);
sf1_gcode0 = (Word16)(Pow2(14, sf1_frac_gcode0, pOverflow));
diff --git a/media/libstagefright/foundation/tests/TypeTraits_test.cpp b/media/libstagefright/foundation/tests/TypeTraits_test.cpp
index 1e2049d..d5383d1 100644
--- a/media/libstagefright/foundation/tests/TypeTraits_test.cpp
+++ b/media/libstagefright/foundation/tests/TypeTraits_test.cpp
@@ -30,7 +30,7 @@
enum IA : int32_t { };
};
-// =========== basic sanity tests for type-support templates
+// =========== basic tests for type-support templates
TEST_F(TypeTraitsTest, StaticTests) {
// ============ is_integral_or_enum
diff --git a/media/libstagefright/omx/1.0/Omx.cpp b/media/libstagefright/omx/1.0/Omx.cpp
index eef9ce3..eb15039 100644
--- a/media/libstagefright/omx/1.0/Omx.cpp
+++ b/media/libstagefright/omx/1.0/Omx.cpp
@@ -22,7 +22,7 @@
#include <media/openmax/OMX_AsString.h>
#include <media/stagefright/omx/OMXUtils.h>
-#include <media/stagefright/omx/OMXMaster.h>
+#include <media/stagefright/omx/OMXStore.h>
#include <media/stagefright/omx/OmxGraphicBufferSource.h>
#include <media/stagefright/omx/1.0/WOmxNode.h>
@@ -41,21 +41,21 @@
constexpr size_t kMaxNodeInstances = (1 << 16);
Omx::Omx() :
- mMaster(new OMXMaster()),
+ mStore(new OMXStore()),
mParser() {
(void)mParser.parseXmlFilesInSearchDirs();
(void)mParser.parseXmlPath(mParser.defaultProfilingResultsXmlPath);
}
Omx::~Omx() {
- delete mMaster;
+ delete mStore;
}
Return<void> Omx::listNodes(listNodes_cb _hidl_cb) {
std::list<::android::IOMX::ComponentInfo> list;
char componentName[256];
for (OMX_U32 index = 0;
- mMaster->enumerateComponents(
+ mStore->enumerateComponents(
componentName, sizeof(componentName), index) == OMX_ErrorNone;
++index) {
list.push_back(::android::IOMX::ComponentInfo());
@@ -63,7 +63,7 @@
info.mName = componentName;
::android::Vector<::android::String8> roles;
OMX_ERRORTYPE err =
- mMaster->getRolesOfComponent(componentName, &roles);
+ mStore->getRolesOfComponent(componentName, &roles);
if (err == OMX_ErrorNone) {
for (OMX_U32 i = 0; i < roles.size(); ++i) {
info.mRoles.push_back(roles[i]);
@@ -101,7 +101,7 @@
this, new LWOmxObserver(observer), name.c_str());
OMX_COMPONENTTYPE *handle;
- OMX_ERRORTYPE err = mMaster->makeComponentInstance(
+ OMX_ERRORTYPE err = mStore->makeComponentInstance(
name.c_str(), &OMXNodeInstance::kCallbacks,
instance.get(), &handle);
@@ -208,7 +208,7 @@
OMX_ERRORTYPE err = OMX_ErrorNone;
if (instance->handle() != NULL) {
- err = mMaster->destroyComponentInstance(
+ err = mStore->destroyComponentInstance(
static_cast<OMX_COMPONENTTYPE*>(instance->handle()));
}
return StatusFromOMXError(err);
diff --git a/media/libstagefright/omx/Android.bp b/media/libstagefright/omx/Android.bp
index 78b4f19..7c372cd 100644
--- a/media/libstagefright/omx/Android.bp
+++ b/media/libstagefright/omx/Android.bp
@@ -7,7 +7,7 @@
double_loadable: true,
srcs: [
- "OMXMaster.cpp",
+ "OMXStore.cpp",
"OMXNodeInstance.cpp",
"OMXUtils.cpp",
"OmxGraphicBufferSource.cpp",
diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp
index ac42373..bebd516 100644
--- a/media/libstagefright/omx/OMXNodeInstance.cpp
+++ b/media/libstagefright/omx/OMXNodeInstance.cpp
@@ -22,7 +22,7 @@
#include <inttypes.h>
#include <media/stagefright/omx/OMXNodeInstance.h>
-#include <media/stagefright/omx/OMXMaster.h>
+#include <media/stagefright/omx/OMXStore.h>
#include <media/stagefright/omx/OMXUtils.h>
#include <android/IOMXBufferSource.h>
diff --git a/media/libstagefright/omx/OMXMaster.cpp b/media/libstagefright/omx/OMXStore.cpp
similarity index 91%
rename from media/libstagefright/omx/OMXMaster.cpp
rename to media/libstagefright/omx/OMXStore.cpp
index 094b1f5..e8fee42 100644
--- a/media/libstagefright/omx/OMXMaster.cpp
+++ b/media/libstagefright/omx/OMXStore.cpp
@@ -15,11 +15,11 @@
*/
//#define LOG_NDEBUG 0
-#define LOG_TAG "OMXMaster"
+#define LOG_TAG "OMXStore"
#include <android-base/properties.h>
#include <utils/Log.h>
-#include <media/stagefright/omx/OMXMaster.h>
+#include <media/stagefright/omx/OMXStore.h>
#include <media/stagefright/omx/SoftOMXPlugin.h>
#include <media/stagefright/foundation/ADebug.h>
@@ -30,7 +30,7 @@
namespace android {
-OMXMaster::OMXMaster() {
+OMXStore::OMXStore() {
pid_t pid = getpid();
char filename[20];
@@ -55,19 +55,19 @@
addPlatformPlugin();
}
-OMXMaster::~OMXMaster() {
+OMXStore::~OMXStore() {
clearPlugins();
}
-void OMXMaster::addVendorPlugin() {
+void OMXStore::addVendorPlugin() {
addPlugin("libstagefrighthw.so");
}
-void OMXMaster::addPlatformPlugin() {
+void OMXStore::addPlatformPlugin() {
addPlugin("libstagefright_softomx_plugin.so");
}
-void OMXMaster::addPlugin(const char *libname) {
+void OMXStore::addPlugin(const char *libname) {
if (::android::base::GetIntProperty("vendor.media.omx", int64_t(1)) == 0) {
return;
}
@@ -99,7 +99,7 @@
}
}
-void OMXMaster::addPlugin(OMXPluginBase *plugin) {
+void OMXStore::addPlugin(OMXPluginBase *plugin) {
Mutex::Autolock autoLock(mLock);
OMX_U32 index = 0;
@@ -126,7 +126,7 @@
}
}
-void OMXMaster::clearPlugins() {
+void OMXStore::clearPlugins() {
Mutex::Autolock autoLock(mLock);
mPluginByComponentName.clear();
@@ -148,7 +148,7 @@
mPlugins.clear();
}
-OMX_ERRORTYPE OMXMaster::makeComponentInstance(
+OMX_ERRORTYPE OMXStore::makeComponentInstance(
const char *name,
const OMX_CALLBACKTYPE *callbacks,
OMX_PTR appData,
@@ -177,7 +177,7 @@
return err;
}
-OMX_ERRORTYPE OMXMaster::destroyComponentInstance(
+OMX_ERRORTYPE OMXStore::destroyComponentInstance(
OMX_COMPONENTTYPE *component) {
Mutex::Autolock autoLock(mLock);
@@ -193,7 +193,7 @@
return plugin->destroyComponentInstance(component);
}
-OMX_ERRORTYPE OMXMaster::enumerateComponents(
+OMX_ERRORTYPE OMXStore::enumerateComponents(
OMX_STRING name,
size_t size,
OMX_U32 index) {
@@ -213,7 +213,7 @@
return OMX_ErrorNone;
}
-OMX_ERRORTYPE OMXMaster::getRolesOfComponent(
+OMX_ERRORTYPE OMXStore::getRolesOfComponent(
const char *name,
Vector<String8> *roles) {
Mutex::Autolock autoLock(mLock);
diff --git a/media/libstagefright/omx/OMXUtils.cpp b/media/libstagefright/omx/OMXUtils.cpp
index 1b8493a..d6d280f 100644
--- a/media/libstagefright/omx/OMXUtils.cpp
+++ b/media/libstagefright/omx/OMXUtils.cpp
@@ -354,7 +354,7 @@
DescribeColorFormat2Params describeParams;
InitOMXParams(&describeParams);
describeParams.eColorFormat = (OMX_COLOR_FORMATTYPE)colorFormat;
- // reasonable dummy values
+ // reasonable initial values (that will be overwritten)
describeParams.nFrameWidth = 128;
describeParams.nFrameHeight = 128;
describeParams.nStride = 128;
diff --git a/media/libstagefright/omx/include/media/stagefright/omx/1.0/Omx.h b/media/libstagefright/omx/include/media/stagefright/omx/1.0/Omx.h
index 5a46b26..84ae511 100644
--- a/media/libstagefright/omx/include/media/stagefright/omx/1.0/Omx.h
+++ b/media/libstagefright/omx/include/media/stagefright/omx/1.0/Omx.h
@@ -27,7 +27,7 @@
namespace android {
-struct OMXMaster;
+struct OMXStore;
struct OMXNodeInstance;
namespace hardware {
@@ -51,7 +51,7 @@
using ::android::sp;
using ::android::wp;
-using ::android::OMXMaster;
+using ::android::OMXStore;
using ::android::OMXNodeInstance;
struct Omx : public IOmx, public hidl_death_recipient {
@@ -73,7 +73,7 @@
status_t freeNode(sp<OMXNodeInstance> const& instance);
protected:
- OMXMaster* mMaster;
+ OMXStore* mStore;
Mutex mLock;
KeyedVector<wp<IBase>, sp<OMXNodeInstance> > mLiveNodes;
KeyedVector<OMXNodeInstance*, wp<IBase> > mNode2Observer;
diff --git a/media/libstagefright/omx/include/media/stagefright/omx/OMXNodeInstance.h b/media/libstagefright/omx/include/media/stagefright/omx/OMXNodeInstance.h
index a761ef6..5f32c9e 100644
--- a/media/libstagefright/omx/include/media/stagefright/omx/OMXNodeInstance.h
+++ b/media/libstagefright/omx/include/media/stagefright/omx/OMXNodeInstance.h
@@ -33,7 +33,7 @@
class GraphicBuffer;
class IOMXBufferSource;
class IOMXObserver;
-struct OMXMaster;
+struct OMXStore;
class OMXBuffer;
using IHidlMemory = hidl::memory::V1_0::IMemory;
using hardware::media::omx::V1_0::implementation::Omx;
diff --git a/media/libstagefright/omx/include/media/stagefright/omx/OMXMaster.h b/media/libstagefright/omx/include/media/stagefright/omx/OMXStore.h
similarity index 88%
rename from media/libstagefright/omx/include/media/stagefright/omx/OMXMaster.h
rename to media/libstagefright/omx/include/media/stagefright/omx/OMXStore.h
index 93eaef1..5d6c3ed 100644
--- a/media/libstagefright/omx/include/media/stagefright/omx/OMXMaster.h
+++ b/media/libstagefright/omx/include/media/stagefright/omx/OMXStore.h
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-#ifndef OMX_MASTER_H_
+#ifndef OMX_STORE_H_
-#define OMX_MASTER_H_
+#define OMX_STORE_H_
#include <media/hardware/OMXPluginBase.h>
@@ -27,9 +27,9 @@
namespace android {
-struct OMXMaster : public OMXPluginBase {
- OMXMaster();
- virtual ~OMXMaster();
+struct OMXStore : public OMXPluginBase {
+ OMXStore();
+ virtual ~OMXStore();
virtual OMX_ERRORTYPE makeComponentInstance(
const char *name,
@@ -66,10 +66,10 @@
void addPlugin(OMXPluginBase *plugin);
void clearPlugins();
- OMXMaster(const OMXMaster &);
- OMXMaster &operator=(const OMXMaster &);
+ OMXStore(const OMXStore &);
+ OMXStore &operator=(const OMXStore &);
};
} // namespace android
-#endif // OMX_MASTER_H_
+#endif // OMX_STORE_H_
diff --git a/media/libstagefright/tests/metadatautils/Android.bp b/media/libstagefright/tests/metadatautils/Android.bp
new file mode 100644
index 0000000..69830fc
--- /dev/null
+++ b/media/libstagefright/tests/metadatautils/Android.bp
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+cc_test {
+ name: "MetaDataUtilsTest",
+ gtest: true,
+
+ srcs: [
+ "MetaDataUtilsTest.cpp",
+ ],
+
+ static_libs: [
+ "libstagefright_metadatautils",
+ "libstagefright_esds",
+ ],
+
+ shared_libs: [
+ "liblog",
+ "libutils",
+ "libmediandk",
+ "libstagefright",
+ "libstagefright_foundation",
+ ],
+
+ cflags: [
+ "-Werror",
+ "-Wall",
+ ],
+
+ sanitize: {
+ cfi: true,
+ misc_undefined: [
+ "unsigned-integer-overflow",
+ "signed-integer-overflow",
+ ],
+ },
+}
diff --git a/media/libstagefright/tests/metadatautils/AndroidTest.xml b/media/libstagefright/tests/metadatautils/AndroidTest.xml
new file mode 100644
index 0000000..d6497f3
--- /dev/null
+++ b/media/libstagefright/tests/metadatautils/AndroidTest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Test module config for MetaDataUtils unit test">
+ <option name="test-suite-tag" value="MetaDataUtilsTest" />
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="false" />
+ <option name="push" value="MetaDataUtilsTest->/data/local/tmp/MetaDataUtilsTest" />
+ <option name="push-file"
+ key="https://storage.googleapis.com/android_media/frameworks/av/media/libstagefright/tests/metadatautils/MetaDataUtilsTestRes-1.0.zip?unzip=true"
+ value="/data/local/tmp/MetaDataUtilsTestRes/" />
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="MetaDataUtilsTest" />
+ <option name="native-test-flag" value="-P /data/local/tmp/MetaDataUtilsTestRes/" />
+ </test>
+</configuration>
diff --git a/media/libstagefright/tests/metadatautils/MetaDataUtilsTest.cpp b/media/libstagefright/tests/metadatautils/MetaDataUtilsTest.cpp
new file mode 100644
index 0000000..9fd5fdb
--- /dev/null
+++ b/media/libstagefright/tests/metadatautils/MetaDataUtilsTest.cpp
@@ -0,0 +1,490 @@
+/*
+ * 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 "MetaDataUtilsTest"
+#include <utils/Log.h>
+
+#include <fstream>
+#include <string>
+
+#include <ESDS.h>
+#include <media/NdkMediaFormat.h>
+#include <media/stagefright/MediaCodecConstants.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MetaDataBase.h>
+#include <media/stagefright/MetaDataUtils.h>
+#include <media/stagefright/foundation/ABitReader.h>
+
+#include "MetaDataUtilsTestEnvironment.h"
+
+constexpr uint8_t kAdtsCsdSize = 7;
+// from AAC specs: https://www.iso.org/standard/43345.html
+constexpr int32_t kSamplingFreq[] = {96000, 88200, 64000, 48000, 44100, 32000,
+ 24000, 22050, 16000, 12000, 11025, 8000};
+constexpr uint8_t kMaxSamplingFreqIndex = sizeof(kSamplingFreq) / sizeof(kSamplingFreq[0]);
+
+static MetaDataUtilsTestEnvironment *gEnv = nullptr;
+
+using namespace android;
+
+class MetaDataValidate {
+ public:
+ MetaDataValidate() : mInputBuffer(nullptr) {}
+
+ ~MetaDataValidate() {
+ if (mInputBuffer) {
+ delete[] mInputBuffer;
+ mInputBuffer = nullptr;
+ }
+ }
+
+ void SetUpMetaDataValidate(string fileName) {
+ struct stat buf;
+ int8_t err = stat(fileName.c_str(), &buf);
+ ASSERT_EQ(err, 0) << "Failed to get file information for file: " << fileName;
+
+ mInputBufferSize = buf.st_size;
+ FILE *inputFilePtr = fopen(fileName.c_str(), "rb+");
+ ASSERT_NE(inputFilePtr, nullptr) << "Failed to open file: " << fileName;
+
+ mInputBuffer = new uint8_t[mInputBufferSize];
+ ASSERT_NE(mInputBuffer, nullptr)
+ << "Failed to allocate memory of size: " << mInputBufferSize;
+
+ int32_t numBytes =
+ fread((char *)mInputBuffer, sizeof(uint8_t), mInputBufferSize, inputFilePtr);
+ ASSERT_EQ(numBytes, mInputBufferSize) << numBytes << " of " << mInputBufferSize << " read";
+
+ fclose(inputFilePtr);
+ }
+
+ size_t mInputBufferSize;
+ const uint8_t *mInputBuffer;
+};
+
+class AvcCSDTest : public ::testing::TestWithParam<
+ tuple<string /*inputFile*/, size_t /*avcWidth*/, size_t /*avcHeight*/>> {
+ public:
+ AvcCSDTest() : mInputBuffer(nullptr) {}
+
+ ~AvcCSDTest() {
+ if (mInputBuffer) {
+ delete[] mInputBuffer;
+ mInputBuffer = nullptr;
+ }
+ }
+ virtual void SetUp() override {
+ tuple<string, size_t, size_t> params = GetParam();
+ string inputFile = gEnv->getRes() + get<0>(params);
+ mFrameWidth = get<1>(params);
+ mFrameHeight = get<2>(params);
+
+ struct stat buf;
+ int8_t err = stat(inputFile.c_str(), &buf);
+ ASSERT_EQ(err, 0) << "Failed to get information for file: " << inputFile;
+
+ mInputBufferSize = buf.st_size;
+ FILE *inputFilePtr = fopen(inputFile.c_str(), "rb+");
+ ASSERT_NE(inputFilePtr, nullptr) << "Failed to open file: " << inputFile;
+
+ mInputBuffer = new uint8_t[mInputBufferSize];
+ ASSERT_NE(mInputBuffer, nullptr)
+ << "Failed to create a buffer of size: " << mInputBufferSize;
+
+ int32_t numBytes =
+ fread((char *)mInputBuffer, sizeof(uint8_t), mInputBufferSize, inputFilePtr);
+ ASSERT_EQ(numBytes, mInputBufferSize) << numBytes << " of " << mInputBufferSize << " read";
+
+ fclose(inputFilePtr);
+ }
+
+ size_t mFrameWidth;
+ size_t mFrameHeight;
+ size_t mInputBufferSize;
+ const uint8_t *mInputBuffer;
+};
+
+class AvcCSDValidateTest : public MetaDataValidate,
+ public ::testing::TestWithParam<string /*inputFile*/> {
+ public:
+ virtual void SetUp() override {
+ string inputFile = gEnv->getRes() + GetParam();
+
+ ASSERT_NO_FATAL_FAILURE(SetUpMetaDataValidate(inputFile));
+ }
+};
+
+class AacCSDTest
+ : public ::testing::TestWithParam<tuple<uint32_t /*profile*/, uint32_t /*samplingFreqIndex*/,
+ uint32_t /*channelConfig*/>> {
+ public:
+ virtual void SetUp() override {
+ tuple<uint32_t, uint32_t, uint32_t> params = GetParam();
+ mAacProfile = get<0>(params);
+ mAacSamplingFreqIndex = get<1>(params);
+ mAacChannelConfig = get<2>(params);
+ }
+
+ uint32_t mAacProfile;
+ uint32_t mAacSamplingFreqIndex;
+ uint32_t mAacChannelConfig;
+};
+
+class AacADTSTest
+ : public ::testing::TestWithParam<
+ tuple<string /*adtsFile*/, uint32_t /*channelCount*/, uint32_t /*sampleRate*/>> {
+ public:
+ AacADTSTest() : mInputBuffer(nullptr) {}
+
+ virtual void SetUp() override {
+ tuple<string, uint32_t, uint32_t> params = GetParam();
+ string fileName = gEnv->getRes() + get<0>(params);
+ mAacChannelCount = get<1>(params);
+ mAacSampleRate = get<2>(params);
+
+ FILE *filePtr = fopen(fileName.c_str(), "r");
+ ASSERT_NE(filePtr, nullptr) << "Failed to open file: " << fileName;
+
+ mInputBuffer = new uint8_t[kAdtsCsdSize];
+ ASSERT_NE(mInputBuffer, nullptr) << "Failed to allocate a memory of size: " << kAdtsCsdSize;
+
+ int32_t numBytes = fread((void *)mInputBuffer, sizeof(uint8_t), kAdtsCsdSize, filePtr);
+ ASSERT_EQ(numBytes, kAdtsCsdSize)
+ << "Failed to read complete file, bytes read: " << numBytes;
+
+ fclose(filePtr);
+ }
+ int32_t mAacChannelCount;
+ int32_t mAacSampleRate;
+ const uint8_t *mInputBuffer;
+};
+
+class AacCSDValidateTest : public MetaDataValidate,
+ public ::testing::TestWithParam<string /*inputFile*/> {
+ public:
+ virtual void SetUp() override {
+ string inputFile = gEnv->getRes() + GetParam();
+
+ ASSERT_NO_FATAL_FAILURE(SetUpMetaDataValidate(inputFile));
+ }
+};
+
+class VorbisTest : public ::testing::TestWithParam<pair<string /*fileName*/, string /*infoFile*/>> {
+ public:
+ virtual void SetUp() override {
+ pair<string, string> params = GetParam();
+ string inputMediaFile = gEnv->getRes() + params.first;
+ mInputFileStream.open(inputMediaFile, ifstream::in);
+ ASSERT_TRUE(mInputFileStream.is_open()) << "Failed to open data file: " << inputMediaFile;
+
+ string inputInfoFile = gEnv->getRes() + params.second;
+ mInfoFileStream.open(inputInfoFile, ifstream::in);
+ ASSERT_TRUE(mInputFileStream.is_open()) << "Failed to open data file: " << inputInfoFile;
+ ASSERT_FALSE(inputInfoFile.empty()) << "Empty info file: " << inputInfoFile;
+ }
+
+ ~VorbisTest() {
+ if (mInputFileStream.is_open()) mInputFileStream.close();
+ if (mInfoFileStream.is_open()) mInfoFileStream.close();
+ }
+
+ ifstream mInputFileStream;
+ ifstream mInfoFileStream;
+};
+
+TEST_P(AvcCSDTest, AvcCSDValidationTest) {
+ AMediaFormat *csdData = AMediaFormat_new();
+ ASSERT_NE(csdData, nullptr) << "Failed to create AMedia format";
+
+ bool status = MakeAVCCodecSpecificData(csdData, mInputBuffer, mInputBufferSize);
+ ASSERT_TRUE(status) << "Failed to make AVC CSD from AMediaFormat";
+
+ int32_t avcWidth = -1;
+ status = AMediaFormat_getInt32(csdData, AMEDIAFORMAT_KEY_WIDTH, &avcWidth);
+ ASSERT_TRUE(status) << "Failed to get avc width";
+ ASSERT_EQ(avcWidth, mFrameWidth);
+
+ int32_t avcHeight = -1;
+ status = AMediaFormat_getInt32(csdData, AMEDIAFORMAT_KEY_HEIGHT, &avcHeight);
+ ASSERT_TRUE(status) << "Failed to get avc height";
+ ASSERT_EQ(avcHeight, mFrameHeight);
+
+ const char *mimeType = "";
+ status = AMediaFormat_getString(csdData, AMEDIAFORMAT_KEY_MIME, &mimeType);
+ ASSERT_TRUE(status) << "Failed to get the mime type";
+ ASSERT_STREQ(mimeType, MEDIA_MIMETYPE_VIDEO_AVC);
+
+ MetaDataBase *metaData = new MetaDataBase();
+ ASSERT_NE(metaData, nullptr) << "Failed to create MetaData Base";
+
+ status = MakeAVCCodecSpecificData(*metaData, mInputBuffer, mInputBufferSize);
+ ASSERT_TRUE(status) << "Failed to make AVC CSD from MetaDataBase";
+
+ avcWidth = -1;
+ status = metaData->findInt32(kKeyWidth, &avcWidth);
+ ASSERT_TRUE(status) << "Failed to find the width";
+ ASSERT_EQ(avcWidth, mFrameWidth);
+
+ avcHeight = -1;
+ status = metaData->findInt32(kKeyHeight, &avcHeight);
+ ASSERT_TRUE(status) << "Failed to find the height";
+ ASSERT_EQ(avcHeight, mFrameHeight);
+
+ void *csdAMediaFormatBuffer = nullptr;
+ size_t csdAMediaFormatSize;
+ status = AMediaFormat_getBuffer(csdData, AMEDIAFORMAT_KEY_CSD_AVC, &csdAMediaFormatBuffer,
+ &csdAMediaFormatSize);
+ ASSERT_TRUE(status) << "Failed to get the CSD from AMediaFormat";
+ ASSERT_NE(csdAMediaFormatBuffer, nullptr) << "Invalid CSD from AMediaFormat";
+
+ const void *csdMetaDataBaseBuffer = nullptr;
+ size_t csdMetaDataBaseSize = 0;
+ uint32_t mediaType;
+ status = metaData->findData(kKeyAVCC, &mediaType, &csdMetaDataBaseBuffer, &csdMetaDataBaseSize);
+ ASSERT_TRUE(status) << "Failed to get the CSD from MetaDataBase";
+ ASSERT_NE(csdMetaDataBaseBuffer, nullptr) << "Invalid CSD from MetaDataBase";
+ ASSERT_GT(csdMetaDataBaseSize, 0) << "CSD size must be greater than 0";
+ ASSERT_EQ(csdMetaDataBaseSize, csdAMediaFormatSize)
+ << "CSD size of MetaData type and AMediaFormat type must be same";
+
+ int32_t result = memcmp(csdAMediaFormatBuffer, csdMetaDataBaseBuffer, csdAMediaFormatSize);
+ ASSERT_EQ(result, 0) << "CSD from AMediaFormat and MetaDataBase do not match";
+
+ delete metaData;
+ AMediaFormat_delete(csdData);
+}
+
+TEST_P(AvcCSDValidateTest, AvcValidateTest) {
+ AMediaFormat *csdData = AMediaFormat_new();
+ ASSERT_NE(csdData, nullptr) << "Failed to create AMedia format";
+
+ bool status = MakeAVCCodecSpecificData(csdData, mInputBuffer, mInputBufferSize);
+ ASSERT_FALSE(status) << "MakeAVCCodecSpecificData with AMediaFormat succeeds with invalid data";
+
+ MetaDataBase *metaData = new MetaDataBase();
+ ASSERT_NE(metaData, nullptr) << "Failed to create MetaData Base";
+
+ status = MakeAVCCodecSpecificData(*metaData, mInputBuffer, mInputBufferSize);
+ ASSERT_FALSE(status) << "MakeAVCCodecSpecificData with MetaDataBase succeeds with invalid data";
+}
+
+TEST_P(AacCSDTest, AacCSDValidationTest) {
+ AMediaFormat *csdData = AMediaFormat_new();
+ ASSERT_NE(csdData, nullptr) << "Failed to create AMedia format";
+
+ ASSERT_GE(mAacSamplingFreqIndex, 0);
+ ASSERT_LT(mAacSamplingFreqIndex, kMaxSamplingFreqIndex);
+ bool status = MakeAACCodecSpecificData(csdData, mAacProfile, mAacSamplingFreqIndex,
+ mAacChannelConfig);
+ ASSERT_TRUE(status) << "Failed to make AAC CSD from AMediaFormat";
+
+ int32_t sampleRate = -1;
+ status = AMediaFormat_getInt32(csdData, AMEDIAFORMAT_KEY_SAMPLE_RATE, &sampleRate);
+ ASSERT_TRUE(status) << "Failed to get sample rate";
+ ASSERT_EQ(kSamplingFreq[mAacSamplingFreqIndex], sampleRate);
+
+ int32_t channelCount = -1;
+ status = AMediaFormat_getInt32(csdData, AMEDIAFORMAT_KEY_CHANNEL_COUNT, &channelCount);
+ ASSERT_TRUE(status) << "Failed to get channel count";
+ ASSERT_EQ(channelCount, mAacChannelConfig);
+
+ const char *mimeType = "";
+ status = AMediaFormat_getString(csdData, AMEDIAFORMAT_KEY_MIME, &mimeType);
+ ASSERT_TRUE(status) << "Failed to get the mime type";
+ ASSERT_STREQ(mimeType, MEDIA_MIMETYPE_AUDIO_AAC);
+
+ MetaDataBase *metaData = new MetaDataBase();
+ ASSERT_NE(metaData, nullptr) << "Failed to create MetaData Base";
+
+ status = MakeAACCodecSpecificData(*metaData, mAacProfile, mAacSamplingFreqIndex,
+ mAacChannelConfig);
+ ASSERT_TRUE(status) << "Failed to make AAC CSD from MetaDataBase";
+
+ sampleRate = -1;
+ status = metaData->findInt32(kKeySampleRate, &sampleRate);
+ ASSERT_TRUE(status) << "Failed to get sampling rate";
+ ASSERT_EQ(kSamplingFreq[mAacSamplingFreqIndex], sampleRate);
+
+ channelCount = -1;
+ status = metaData->findInt32(kKeyChannelCount, &channelCount);
+ ASSERT_TRUE(status) << "Failed to get channel count";
+ ASSERT_EQ(channelCount, mAacChannelConfig);
+
+ mimeType = "";
+ status = metaData->findCString(kKeyMIMEType, &mimeType);
+ ASSERT_TRUE(status) << "Failed to get mime type";
+ ASSERT_STREQ(mimeType, MEDIA_MIMETYPE_AUDIO_AAC);
+
+ void *csdAMediaFormatBuffer = nullptr;
+ size_t csdAMediaFormatSize = 0;
+ status = AMediaFormat_getBuffer(csdData, AMEDIAFORMAT_KEY_CSD_0, &csdAMediaFormatBuffer,
+ &csdAMediaFormatSize);
+ ASSERT_TRUE(status) << "Failed to get the AMediaFormat CSD";
+ ASSERT_GT(csdAMediaFormatSize, 0) << "CSD size must be greater than 0";
+ ASSERT_NE(csdAMediaFormatBuffer, nullptr) << "Invalid CSD found";
+
+ const void *csdMetaDataBaseBuffer;
+ size_t csdMetaDataBaseSize = 0;
+ uint32_t mediaType;
+ status = metaData->findData(kKeyESDS, &mediaType, &csdMetaDataBaseBuffer, &csdMetaDataBaseSize);
+ ASSERT_TRUE(status) << "Failed to get the ESDS data from MetaDataBase";
+ ASSERT_GT(csdMetaDataBaseSize, 0) << "CSD size must be greater than 0";
+
+ ESDS esds(csdMetaDataBaseBuffer, csdMetaDataBaseSize);
+ status_t result = esds.getCodecSpecificInfo(&csdMetaDataBaseBuffer, &csdMetaDataBaseSize);
+ ASSERT_EQ(result, (status_t)OK) << "Failed to get CSD from ESDS data";
+ ASSERT_NE(csdMetaDataBaseBuffer, nullptr) << "Invalid CSD found";
+ ASSERT_EQ(csdAMediaFormatSize, csdMetaDataBaseSize)
+ << "CSD size do not match between AMediaFormat type and MetaDataBase type";
+
+ int32_t memcmpResult =
+ memcmp(csdAMediaFormatBuffer, csdMetaDataBaseBuffer, csdAMediaFormatSize);
+ ASSERT_EQ(memcmpResult, 0) << "AMediaFormat and MetaDataBase CSDs do not match";
+
+ AMediaFormat_delete(csdData);
+ delete metaData;
+}
+
+TEST_P(AacADTSTest, AacADTSValidationTest) {
+ MetaDataBase *metaData = new MetaDataBase();
+ ASSERT_NE(metaData, nullptr) << "Failed to create meta data";
+
+ bool status = MakeAACCodecSpecificData(*metaData, mInputBuffer, kAdtsCsdSize);
+ ASSERT_TRUE(status) << "Failed to make AAC CSD from MetaDataBase";
+
+ int32_t sampleRate = -1;
+ status = metaData->findInt32(kKeySampleRate, &sampleRate);
+ ASSERT_TRUE(status) << "Failed to get sampling rate";
+ ASSERT_EQ(sampleRate, mAacSampleRate);
+
+ int32_t channelCount = -1;
+ status = metaData->findInt32(kKeyChannelCount, &channelCount);
+ ASSERT_TRUE(status) << "Failed to get channel count";
+ ASSERT_EQ(channelCount, mAacChannelCount);
+
+ const char *mimeType = "";
+ status = metaData->findCString(kKeyMIMEType, &mimeType);
+ ASSERT_TRUE(status) << "Failed to get mime type";
+ ASSERT_STREQ(mimeType, MEDIA_MIMETYPE_AUDIO_AAC);
+
+ delete metaData;
+}
+
+TEST_P(AacCSDValidateTest, AacInvalidInputTest) {
+ MetaDataBase *metaData = new MetaDataBase();
+ ASSERT_NE(metaData, nullptr) << "Failed to create meta data";
+
+ bool status = MakeAACCodecSpecificData(*metaData, mInputBuffer, kAdtsCsdSize);
+ ASSERT_FALSE(status) << "MakeAACCodecSpecificData succeeds with invalid data";
+}
+
+TEST_P(VorbisTest, VorbisCommentTest) {
+ string line;
+ string tag;
+ string key;
+ string value;
+ size_t commentLength;
+ bool status;
+
+ while (getline(mInfoFileStream, line)) {
+ istringstream stringLine(line);
+ stringLine >> tag >> key >> value >> commentLength;
+ ASSERT_GT(commentLength, 0) << "Vorbis comment size must be greater than 0";
+
+ string comment;
+ string dataLine;
+
+ getline(mInputFileStream, dataLine);
+ istringstream dataStringLine(dataLine);
+ dataStringLine >> comment;
+
+ char *buffer = strndup(comment.c_str(), commentLength);
+ ASSERT_NE(buffer, nullptr) << "Failed to allocate buffer of size: " << commentLength;
+
+ AMediaFormat *fileMeta = AMediaFormat_new();
+ ASSERT_NE(fileMeta, nullptr) << "Failed to create AMedia format";
+
+ parseVorbisComment(fileMeta, buffer, commentLength);
+ free(buffer);
+
+ if (!strncasecmp(tag.c_str(), "ANDROID_HAPTIC", sizeof(tag))) {
+ int32_t numChannelExpected = stoi(value);
+ int32_t numChannelFound = -1;
+ status = AMediaFormat_getInt32(fileMeta, key.c_str(), &numChannelFound);
+ ASSERT_TRUE(status) << "Failed to get the channel count";
+ ASSERT_EQ(numChannelExpected, numChannelFound);
+ } else if (!strncasecmp(tag.c_str(), "ANDROID_LOOP", sizeof(tag))) {
+ int32_t loopExpected = !value.compare("true");
+ int32_t loopFound = -1;
+
+ status = AMediaFormat_getInt32(fileMeta, "loop", &loopFound);
+ ASSERT_TRUE(status) << "Failed to get the loop count";
+ ASSERT_EQ(loopExpected, loopFound);
+ } else {
+ const char *tagValue = "";
+ status = AMediaFormat_getString(fileMeta, key.c_str(), &tagValue);
+ ASSERT_TRUE(status) << "Failed to get the tag value";
+ ASSERT_STREQ(value.c_str(), tagValue);
+ }
+ AMediaFormat_delete(fileMeta);
+ }
+}
+
+INSTANTIATE_TEST_SUITE_P(MetaDataUtilsTestAll, AvcCSDTest,
+ ::testing::Values(make_tuple("sps_pps_userdata.h264", 8, 8),
+ make_tuple("sps_userdata_pps.h264", 8, 8),
+ make_tuple("sps_pps_sps_pps.h264", 8, 8)));
+
+// TODO(b/158067691): Add invalid test vectors with incomplete PPS or no PPS
+INSTANTIATE_TEST_SUITE_P(MetaDataUtilsTestAll, AvcCSDValidateTest,
+ ::testing::Values("sps_pps_only_startcode.h264",
+ "sps_incomplete_pps.h264",
+ // TODO(b/158067691) "sps_pps_incomplete.h264",
+ "randomdata.h264",
+ // TODO(b/158067691) "sps.h264",
+ "pps.h264"));
+
+INSTANTIATE_TEST_SUITE_P(MetaDataUtilsTestAll, AacCSDTest,
+ ::testing::Values(make_tuple(AACObjectMain, 1, 1)));
+
+INSTANTIATE_TEST_SUITE_P(MetaDataUtilsTestAll, AacADTSTest,
+ ::testing::Values(make_tuple("loudsoftaacadts", 1, 44100)));
+
+INSTANTIATE_TEST_SUITE_P(MetaDataUtilsTestAll, AacCSDValidateTest,
+ ::testing::Values("loudsoftaacadts_invalidheader",
+ "loudsoftaacadts_invalidprofile",
+ "loudsoftaacadts_invalidchannelconfig"));
+
+// TODO(b/157974508) Add test vector for vorbis thumbnail tag
+// Info file contains TAG, Key, Value and size of the vorbis comment
+INSTANTIATE_TEST_SUITE_P(
+ MetaDataUtilsTestAll, VorbisTest,
+ ::testing::Values(make_pair("vorbiscomment_sintel.dat", "vorbiscomment_sintel.info"),
+ make_pair("vorbiscomment_album.dat", "vorbiscomment_album.info"),
+ make_pair("vorbiscomment_loop.dat", "vorbiscomment_loop.info")));
+
+int main(int argc, char **argv) {
+ gEnv = new MetaDataUtilsTestEnvironment();
+ ::testing::AddGlobalTestEnvironment(gEnv);
+ ::testing::InitGoogleTest(&argc, argv);
+ int status = gEnv->initFromOptions(argc, argv);
+ if (status == 0) {
+ status = RUN_ALL_TESTS();
+ ALOGV("Test result = %d\n", status);
+ }
+ return status;
+}
diff --git a/media/libstagefright/tests/metadatautils/MetaDataUtilsTestEnvironment.h b/media/libstagefright/tests/metadatautils/MetaDataUtilsTestEnvironment.h
new file mode 100644
index 0000000..4d642bc
--- /dev/null
+++ b/media/libstagefright/tests/metadatautils/MetaDataUtilsTestEnvironment.h
@@ -0,0 +1,73 @@
+/*
+ * 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 __METADATA_UTILS_TEST_ENVIRONMENT_H__
+#define __METADATA_UTILS_TEST_ENVIRONMENT_H__
+
+#include <gtest/gtest.h>
+
+#include <getopt.h>
+
+using namespace std;
+
+class MetaDataUtilsTestEnvironment : public::testing::Environment {
+ public:
+ MetaDataUtilsTestEnvironment() : res("/data/local/tmp/") {}
+
+ // Parses the command line arguments
+ int initFromOptions(int argc, char **argv);
+
+ void setRes(const char *_res) { res = _res; }
+
+ const string getRes() const { return res; }
+
+ private:
+ string res;
+};
+
+int MetaDataUtilsTestEnvironment::initFromOptions(int argc, char **argv) {
+ static struct option options[] = {{"path", required_argument, 0, 'P'}, {0, 0, 0, 0}};
+
+ while (true) {
+ int index = 0;
+ int c = getopt_long(argc, argv, "P:", options, &index);
+ if (c == -1) {
+ break;
+ }
+
+ switch (c) {
+ case 'P': {
+ setRes(optarg);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ if (optind < argc) {
+ fprintf(stderr,
+ "unrecognized option: %s\n\n"
+ "usage: %s <gtest options> <test options>\n\n"
+ "test options are:\n\n"
+ "-P, --path: Resource files directory location\n",
+ argv[optind ?: 1], argv[0]);
+ return 2;
+ }
+ return 0;
+}
+
+#endif // __METADATA_UTILS_TEST_ENVIRONMENT_H__
diff --git a/media/libstagefright/tests/metadatautils/README.md b/media/libstagefright/tests/metadatautils/README.md
new file mode 100644
index 0000000..0862a07
--- /dev/null
+++ b/media/libstagefright/tests/metadatautils/README.md
@@ -0,0 +1,39 @@
+## Media Testing ##
+---
+#### MetaDataUtils Test
+The MetaDataUtils Unit Test Suite validates the libstagefright_metadatautils library available in libstagefright.
+
+Run the following steps to build the test suite:
+```
+m MetaDataUtilsTest
+```
+
+The 32-bit binaries will be created in the following path : ${OUT}/data/nativetest/
+
+The 64-bit binaries will be created in the following path : ${OUT}/data/nativetest64/
+
+To test 64-bit binary push binaries from nativetest64.
+```
+adb push ${OUT}/data/nativetest64/MetaDataUtilsTest/MetaDataUtilsTest /data/local/tmp/
+```
+
+To test 32-bit binary push binaries from nativetest.
+```
+adb push ${OUT}/data/nativetest/MetaDataUtilsTest/MetaDataUtilsTest /data/local/tmp/
+```
+
+The resource file for the tests is taken from [here](https://storage.googleapis.com/android_media/frameworks/av/media/libstagefright/tests/metadatautils/MetaDataUtilsTestRes-1.0.zip). Download, unzip and push these files into device for testing.
+
+```
+adb push MetaDataUtilsTestRes-1.0 /data/local/tmp/
+```
+
+usage: MetaDataUtilsTest -P \<path_to_folder\>
+```
+adb shell /data/local/tmp/MetaDataUtilsTest -P /data/local/tmp/MetaDataUtilsTestRes-1.0/
+```
+Alternatively, the test can also be run using atest command.
+
+```
+atest MetaDataUtilsTest -- --enable-module-dynamic-download=true
+```
diff --git a/media/utils/EventLogTags.logtags b/media/utils/EventLogTags.logtags
index 67f0ea8..c397f34 100644
--- a/media/utils/EventLogTags.logtags
+++ b/media/utils/EventLogTags.logtags
@@ -31,7 +31,7 @@
# 6: Percent
# Default value for data of type int/long is 2 (bytes).
#
-# See system/core/logcat/event.logtags for the master copy of the tags.
+# See system/core/logcat/event.logtags for the original definition of the tags.
# 61000 - 61199 reserved for audioserver
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index f014209..728c38d 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -61,6 +61,7 @@
#include <system/audio_effects/effect_visualizer.h>
#include <system/audio_effects/effect_ns.h>
#include <system/audio_effects/effect_aec.h>
+#include <system/audio_effects/effect_hapticgenerator.h>
#include <audio_utils/primitives.h>
@@ -398,7 +399,7 @@
return ret;
}
}
- return AudioMixer::HAPTIC_SCALE_MUTE;
+ return static_cast<int>(os::HapticScale::MUTE);
}
/* static */
@@ -684,8 +685,8 @@
sp<NBLog::Writer> AudioFlinger::newWriter_l(size_t size, const char *name)
{
- // If there is no memory allocated for logs, return a dummy writer that does nothing.
- // Similarly if we can't contact the media.log service, also return a dummy writer.
+ // If there is no memory allocated for logs, return a no-op writer that does nothing.
+ // Similarly if we can't contact the media.log service, also return a no-op writer.
if (mLogMemoryDealer == 0 || sMediaLogService == 0) {
return new NBLog::Writer();
}
@@ -711,7 +712,7 @@
}
}
// Even after garbage-collecting all old writers, there is still not enough memory,
- // so return a dummy writer
+ // so return a no-op writer
return new NBLog::Writer();
}
success:
@@ -1283,9 +1284,9 @@
}
// Now set the master mute in each playback thread. Playback threads
- // assigned to HALs which do not have master mute support will apply master
- // mute during the mix operation. Threads with HALs which do support master
- // mute will simply ignore the setting.
+ // assigned to HALs which do not have master mute support will apply master mute
+ // during the mix operation. Threads with HALs which do support master mute
+ // will simply ignore the setting.
Vector<VolumeInterface *> volumeInterfaces = getAllVolumeInterfaces_l();
for (size_t i = 0; i < volumeInterfaces.size(); i++) {
volumeInterfaces[i]->setMasterMute(muted);
@@ -3297,6 +3298,16 @@
return minThread;
}
+AudioFlinger::ThreadBase *AudioFlinger::hapticPlaybackThread_l() const {
+ for (size_t i = 0; i < mPlaybackThreads.size(); ++i) {
+ PlaybackThread *thread = mPlaybackThreads.valueAt(i).get();
+ if (thread->hapticChannelMask() != AUDIO_CHANNEL_NONE) {
+ return thread;
+ }
+ }
+ return nullptr;
+}
+
sp<AudioFlinger::SyncEvent> AudioFlinger::createSyncEvent(AudioSystem::sync_event_t type,
audio_session_t triggerSession,
audio_session_t listenerSession,
@@ -3538,6 +3549,16 @@
goto Exit;
}
+ const bool hapticPlaybackRequired = EffectModule::isHapticGenerator(&desc.type);
+ if (hapticPlaybackRequired
+ && (sessionId == AUDIO_SESSION_DEVICE
+ || sessionId == AUDIO_SESSION_OUTPUT_MIX
+ || sessionId == AUDIO_SESSION_OUTPUT_STAGE)) {
+ // haptic-generating effect is only valid when the session id is a general session id
+ lStatus = INVALID_OPERATION;
+ goto Exit;
+ }
+
// return effect descriptor
*pDesc = desc;
if (io == AUDIO_IO_HANDLE_NONE && sessionId == AUDIO_SESSION_OUTPUT_MIX) {
@@ -3612,7 +3633,17 @@
// allow only one effect chain per sessionId on mPlaybackThreads.
for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
const audio_io_handle_t checkIo = mPlaybackThreads.keyAt(i);
- if (io == checkIo) continue;
+ if (io == checkIo) {
+ if (hapticPlaybackRequired
+ && mPlaybackThreads.valueAt(i)
+ ->hapticChannelMask() == AUDIO_CHANNEL_NONE) {
+ ALOGE("%s: haptic playback thread is required while the required playback "
+ "thread(io=%d) doesn't support", __func__, (int)io);
+ lStatus = BAD_VALUE;
+ goto Exit;
+ }
+ continue;
+ }
const uint32_t sessionType =
mPlaybackThreads.valueAt(i)->hasAudioSession(sessionId);
if ((sessionType & ThreadBase::EFFECT_SESSION) != 0) {
@@ -3649,6 +3680,20 @@
// create effect on selected output thread
bool pinned = !audio_is_global_session(sessionId) && isSessionAcquired_l(sessionId);
+ ThreadBase *oriThread = nullptr;
+ if (hapticPlaybackRequired && thread->hapticChannelMask() == AUDIO_CHANNEL_NONE) {
+ ThreadBase *hapticThread = hapticPlaybackThread_l();
+ if (hapticThread == nullptr) {
+ ALOGE("%s haptic thread not found while it is required", __func__);
+ lStatus = INVALID_OPERATION;
+ goto Exit;
+ }
+ if (hapticThread != thread) {
+ // Force to use haptic thread for haptic-generating effect.
+ oriThread = thread;
+ thread = hapticThread;
+ }
+ }
handle = thread->createEffect_l(client, effectClient, priority, sessionId,
&desc, enabled, &lStatus, pinned, probe);
if (lStatus != NO_ERROR && lStatus != ALREADY_EXISTS) {
@@ -3658,6 +3703,11 @@
} else {
// handle must be valid here, but check again to be safe.
if (handle.get() != nullptr && id != nullptr) *id = handle->id();
+ // Invalidate audio session when haptic playback is created.
+ if (hapticPlaybackRequired && oriThread != nullptr) {
+ // invalidateTracksForAudioSession will trigger locking the thread.
+ oriThread->invalidateTracksForAudioSession(sessionId);
+ }
}
}
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index ef7b1ab..d3ad908 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -98,6 +98,7 @@
#include <private/media/AudioTrackShared.h>
#include <vibrator/ExternalVibration.h>
+#include <vibrator/ExternalVibrationUtils.h>
#include "android/media/BnAudioRecord.h"
@@ -756,6 +757,8 @@
sp<ThreadBase> getEffectThread_l(audio_session_t sessionId, int effectId);
+ ThreadBase *hapticPlaybackThread_l() const;
+
void removeClient_l(pid_t pid);
void removeNotificationClient(pid_t pid);
diff --git a/services/audioflinger/DeviceEffectManager.h b/services/audioflinger/DeviceEffectManager.h
index 81e6065..c6d2110 100644
--- a/services/audioflinger/DeviceEffectManager.h
+++ b/services/audioflinger/DeviceEffectManager.h
@@ -165,6 +165,7 @@
uint32_t sampleRate() const override { return 0; }
audio_channel_mask_t channelMask() const override { return AUDIO_CHANNEL_NONE; }
uint32_t channelCount() const override { return 0; }
+ audio_channel_mask_t hapticChannelMask() const override { return AUDIO_CHANNEL_NONE; }
size_t frameCount() const override { return 0; }
uint32_t latency() const override { return 0; }
diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp
index 3dfeb83..9ee47c9 100644
--- a/services/audioflinger/Effects.cpp
+++ b/services/audioflinger/Effects.cpp
@@ -25,6 +25,7 @@
#include <utils/Log.h>
#include <system/audio_effects/effect_aec.h>
#include <system/audio_effects/effect_dynamicsprocessing.h>
+#include <system/audio_effects/effect_hapticgenerator.h>
#include <system/audio_effects/effect_ns.h>
#include <system/audio_effects/effect_visualizer.h>
#include <audio_utils/channels.h>
@@ -879,6 +880,11 @@
}
#endif
}
+ if (isHapticGenerator()) {
+ audio_channel_mask_t hapticChannelMask = mCallback->hapticChannelMask();
+ mConfig.inputCfg.channels |= hapticChannelMask;
+ mConfig.outputCfg.channels |= hapticChannelMask;
+ }
mInChannelCountRequested =
audio_channel_count_from_out_mask(mConfig.inputCfg.channels);
mOutChannelCountRequested =
@@ -1522,6 +1528,42 @@
return mOffloaded;
}
+/*static*/
+bool AudioFlinger::EffectModule::isHapticGenerator(const effect_uuid_t *type) {
+ return memcmp(type, FX_IID_HAPTICGENERATOR, sizeof(effect_uuid_t)) == 0;
+}
+
+bool AudioFlinger::EffectModule::isHapticGenerator() const {
+ return isHapticGenerator(&mDescriptor.type);
+}
+
+status_t AudioFlinger::EffectModule::setHapticIntensity(int id, int intensity)
+{
+ if (mStatus != NO_ERROR) {
+ return mStatus;
+ }
+ if (!isHapticGenerator()) {
+ ALOGW("Should not set haptic intensity for effects that are not HapticGenerator");
+ return INVALID_OPERATION;
+ }
+
+ uint32_t buf32[sizeof(effect_param_t) / sizeof(uint32_t) + 3];
+ effect_param_t *param = (effect_param_t*) buf32;
+ param->psize = sizeof(int32_t);
+ param->vsize = sizeof(int32_t) * 2;
+ *(int32_t*)param->data = HG_PARAM_HAPTIC_INTENSITY;
+ *((int32_t*)param->data + 1) = id;
+ *((int32_t*)param->data + 2) = intensity;
+ uint32_t size = sizeof(int32_t);
+ status_t status = command(
+ EFFECT_CMD_SET_PARAM, sizeof(effect_param_t) + param->psize + param->vsize,
+ param, &size, ¶m->status);
+ if (status == NO_ERROR) {
+ status = param->status;
+ }
+ return status;
+}
+
static std::string dumpInOutBuffer(bool isInput, const sp<EffectBufferHalInterface> &buffer) {
std::stringstream ss;
@@ -2385,6 +2427,25 @@
}
}
+// containsHapticGeneratingEffect_l must be called with ThreadBase::mLock or EffectChain::mLock held
+bool AudioFlinger::EffectChain::containsHapticGeneratingEffect_l()
+{
+ for (size_t i = 0; i < mEffects.size(); ++i) {
+ if (mEffects[i]->isHapticGenerator()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void AudioFlinger::EffectChain::setHapticIntensity_l(int id, int intensity)
+{
+ Mutex::Autolock _l(mLock);
+ for (size_t i = 0; i < mEffects.size(); ++i) {
+ mEffects[i]->setHapticIntensity(id, intensity);
+ }
+}
+
void AudioFlinger::EffectChain::syncHalEffectsState()
{
Mutex::Autolock _l(mLock);
@@ -2839,6 +2900,14 @@
return t->channelCount();
}
+audio_channel_mask_t AudioFlinger::EffectChain::EffectCallback::hapticChannelMask() const {
+ sp<ThreadBase> t = mThread.promote();
+ if (t == nullptr) {
+ return AUDIO_CHANNEL_NONE;
+ }
+ return t->hapticChannelMask();
+}
+
size_t AudioFlinger::EffectChain::EffectCallback::frameCount() const {
sp<ThreadBase> t = mThread.promote();
if (t == nullptr) {
diff --git a/services/audioflinger/Effects.h b/services/audioflinger/Effects.h
index 2826297..3cc5a44 100644
--- a/services/audioflinger/Effects.h
+++ b/services/audioflinger/Effects.h
@@ -36,6 +36,7 @@
virtual uint32_t sampleRate() const = 0;
virtual audio_channel_mask_t channelMask() const = 0;
virtual uint32_t channelCount() const = 0;
+ virtual audio_channel_mask_t hapticChannelMask() const = 0;
virtual size_t frameCount() const = 0;
// Non trivial methods usually implemented with help from ThreadBase:
@@ -257,6 +258,11 @@
sp<EffectModule> asEffectModule() override { return this; }
+ static bool isHapticGenerator(const effect_uuid_t* type);
+ bool isHapticGenerator() const;
+
+ status_t setHapticIntensity(int id, int intensity);
+
void dump(int fd, const Vector<String16>& args);
private:
@@ -503,6 +509,10 @@
// isCompatibleWithThread_l() must be called with thread->mLock held
bool isCompatibleWithThread_l(const sp<ThreadBase>& thread) const;
+ bool containsHapticGeneratingEffect_l();
+
+ void setHapticIntensity_l(int id, int intensity);
+
sp<EffectCallbackInterface> effectCallback() const { return mEffectCallback; }
wp<ThreadBase> thread() const { return mEffectCallback->thread(); }
@@ -534,6 +544,7 @@
uint32_t sampleRate() const override;
audio_channel_mask_t channelMask() const override;
uint32_t channelCount() const override;
+ audio_channel_mask_t hapticChannelMask() const override;
size_t frameCount() const override;
uint32_t latency() const override;
@@ -685,6 +696,7 @@
uint32_t sampleRate() const override;
audio_channel_mask_t channelMask() const override;
uint32_t channelCount() const override;
+ audio_channel_mask_t hapticChannelMask() const override { return AUDIO_CHANNEL_NONE; }
size_t frameCount() const override { return 0; }
uint32_t latency() const override { return 0; }
diff --git a/services/audioflinger/FastMixerState.h b/services/audioflinger/FastMixerState.h
index 396c797..857d3de 100644
--- a/services/audioflinger/FastMixerState.h
+++ b/services/audioflinger/FastMixerState.h
@@ -23,6 +23,7 @@
#include <media/ExtendedAudioBufferProvider.h>
#include <media/nbaio/NBAIO.h>
#include <media/nblog/NBLog.h>
+#include <vibrator/ExternalVibrationUtils.h>
#include "FastThreadState.h"
namespace android {
@@ -49,8 +50,7 @@
audio_format_t mFormat; // track format
int mGeneration; // increment when any field is assigned
bool mHapticPlaybackEnabled = false; // haptic playback is enabled or not
- AudioMixer::haptic_intensity_t mHapticIntensity = AudioMixer::HAPTIC_SCALE_MUTE; // intensity of
- // haptic data
+ os::HapticScale mHapticIntensity = os::HapticScale::MUTE; // intensity of haptic data
};
// Represents a single state of the fast mixer
diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h
index d8eebf3..d05c8b8 100644
--- a/services/audioflinger/PlaybackTracks.h
+++ b/services/audioflinger/PlaybackTracks.h
@@ -159,12 +159,12 @@
mHapticPlaybackEnabled = hapticPlaybackEnabled;
}
/** Return at what intensity to play haptics, used in mixer. */
- AudioMixer::haptic_intensity_t getHapticIntensity() const { return mHapticIntensity; }
+ os::HapticScale getHapticIntensity() const { return mHapticIntensity; }
/** Set intensity of haptic playback, should be set after querying vibrator service. */
- void setHapticIntensity(AudioMixer::haptic_intensity_t hapticIntensity) {
- if (AudioMixer::isValidHapticIntensity(hapticIntensity)) {
+ void setHapticIntensity(os::HapticScale hapticIntensity) {
+ if (os::isValidHapticScale(hapticIntensity)) {
mHapticIntensity = hapticIntensity;
- setHapticPlaybackEnabled(mHapticIntensity != AudioMixer::HAPTIC_SCALE_MUTE);
+ setHapticPlaybackEnabled(mHapticIntensity != os::HapticScale::MUTE);
}
}
sp<os::ExternalVibration> getExternalVibration() const { return mExternalVibration; }
@@ -265,7 +265,7 @@
bool mHapticPlaybackEnabled = false; // indicates haptic playback enabled or not
// intensity to play haptic data
- AudioMixer::haptic_intensity_t mHapticIntensity = AudioMixer::HAPTIC_SCALE_MUTE;
+ os::HapticScale mHapticIntensity = os::HapticScale::MUTE;
class AudioVibrationController : public os::BnExternalVibrationController {
public:
explicit AudioVibrationController(Track* track) : mTrack(track) {}
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index c252d77..cdf3702 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -1243,6 +1243,11 @@
return BAD_VALUE;
}
}
+
+ if (EffectModule::isHapticGenerator(&desc->type)) {
+ ALOGE("%s(): HapticGenerator is not supported in RecordThread", __func__);
+ return BAD_VALUE;
+ }
return NO_ERROR;
}
@@ -1262,6 +1267,12 @@
return NO_ERROR;
}
+ if (EffectModule::isHapticGenerator(&desc->type) && mHapticChannelCount == 0) {
+ ALOGW("%s: thread doesn't support haptic playback while the effect is HapticGenerator",
+ __func__);
+ return BAD_VALUE;
+ }
+
switch (mType) {
case MIXER: {
#ifndef MULTICHANNEL_EFFECT_CHAIN
@@ -2527,15 +2538,17 @@
track->sharedBuffer() != 0 ? Track::FS_FILLED : Track::FS_FILLING;
}
- if ((track->channelMask() & AUDIO_CHANNEL_HAPTIC_ALL) != AUDIO_CHANNEL_NONE
- && mHapticChannelMask != AUDIO_CHANNEL_NONE) {
+ sp<EffectChain> chain = getEffectChain_l(track->sessionId());
+ if (mHapticChannelMask != AUDIO_CHANNEL_NONE
+ && ((track->channelMask() & AUDIO_CHANNEL_HAPTIC_ALL) != AUDIO_CHANNEL_NONE
+ || (chain != nullptr && chain->containsHapticGeneratingEffect_l()))) {
// Unlock due to VibratorService will lock for this call and will
// call Tracks.mute/unmute which also require thread's lock.
mLock.unlock();
const int intensity = AudioFlinger::onExternalVibrationStart(
track->getExternalVibration());
mLock.lock();
- track->setHapticIntensity(static_cast<AudioMixer::haptic_intensity_t>(intensity));
+ track->setHapticIntensity(static_cast<os::HapticScale>(intensity));
// Haptic playback should be enabled by vibrator service.
if (track->getHapticPlaybackEnabled()) {
// Disable haptic playback of all active track to ensure only
@@ -2544,12 +2557,16 @@
t->setHapticPlaybackEnabled(false);
}
}
+
+ // Set haptic intensity for effect
+ if (chain != nullptr) {
+ chain->setHapticIntensity_l(track->id(), intensity);
+ }
}
track->mResetDone = false;
track->mPresentationCompleteFrames = 0;
mActiveTracks.add(track);
- sp<EffectChain> chain = getEffectChain_l(track->sessionId());
if (chain != 0) {
ALOGV("addTrack_l() starting track on chain %p for session %d", chain.get(),
track->sessionId());
@@ -3732,9 +3749,15 @@
// Determine which session to pick up haptic data.
// This must be done under the same lock as prepareTracks_l().
+ // The haptic data from the effect is at a higher priority than the one from track.
// TODO: Write haptic data directly to sink buffer when mixing.
if (mHapticChannelCount > 0 && effectChains.size() > 0) {
for (const auto& track : mActiveTracks) {
+ sp<EffectChain> effectChain = getEffectChain_l(track->sessionId());
+ if (effectChain != nullptr && effectChain->containsHapticGeneratingEffect_l()) {
+ activeHapticSessionId = track->sessionId();
+ break;
+ }
if (track->getHapticPlaybackEnabled()) {
activeHapticSessionId = track->sessionId();
break;
@@ -4104,13 +4127,20 @@
// remove from our tracks vector
removeTrack_l(track);
}
- if ((track->channelMask() & AUDIO_CHANNEL_HAPTIC_ALL) != AUDIO_CHANNEL_NONE
- && mHapticChannelCount > 0) {
+ if (mHapticChannelCount > 0 &&
+ ((track->channelMask() & AUDIO_CHANNEL_HAPTIC_ALL) != AUDIO_CHANNEL_NONE
+ || (chain != nullptr && chain->containsHapticGeneratingEffect_l()))) {
mLock.unlock();
// Unlock due to VibratorService will lock for this call and will
// call Tracks.mute/unmute which also require thread's lock.
AudioFlinger::onExternalVibrationStop(track->getExternalVibration());
mLock.lock();
+
+ // When the track is stop, set the haptic intensity as MUTE
+ // for the HapticGenerator effect.
+ if (chain != nullptr) {
+ chain->setHapticIntensity_l(track->id(), static_cast<int>(os::HapticScale::MUTE));
+ }
}
}
}
@@ -4453,7 +4483,7 @@
// audio to FastMixer
fastTrack->mFormat = mFormat; // mPipeSink format for audio to FastMixer
fastTrack->mHapticPlaybackEnabled = mHapticChannelMask != AUDIO_CHANNEL_NONE;
- fastTrack->mHapticIntensity = AudioMixer::HAPTIC_SCALE_NONE;
+ fastTrack->mHapticIntensity = os::HapticScale::NONE;
fastTrack->mGeneration++;
state->mFastTracksGen++;
state->mTrackMask = 1;
@@ -7312,7 +7342,7 @@
const ssize_t availableToRead = mPipeSource->availableToRead();
if (availableToRead >= 0) {
- // PipeSource is the master clock. It is up to the AudioRecord client to keep up.
+ // PipeSource is the primary clock. It is up to the AudioRecord client to keep up.
LOG_ALWAYS_FATAL_IF((size_t)availableToRead > mPipeFramesP2,
"more frames to read than fifo size, %zd > %zu",
availableToRead, mPipeFramesP2);
@@ -9393,6 +9423,11 @@
return BAD_VALUE;
}
+ if (EffectModule::isHapticGenerator(&desc->type)) {
+ ALOGE("%s(): HapticGenerator is not supported for MmapThread", __func__);
+ return BAD_VALUE;
+ }
+
return NO_ERROR;
}
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index f81387e..2e81ae7 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -272,6 +272,7 @@
// Called by AudioFlinger::frameCount(audio_io_handle_t output) and effects,
// and returns the [normal mix] buffer's frame count.
virtual size_t frameCount() const = 0;
+ virtual audio_channel_mask_t hapticChannelMask() const { return AUDIO_CHANNEL_NONE; }
virtual uint32_t latency_l() const { return 0; }
virtual void setVolumeForOutput_l(float left __unused, float right __unused) const {}
@@ -478,6 +479,25 @@
void onEffectEnable(const sp<EffectModule>& effect);
void onEffectDisable();
+ // invalidateTracksForAudioSession_l must be called with holding mLock.
+ virtual void invalidateTracksForAudioSession_l(audio_session_t sessionId __unused) const { }
+ // Invalidate all the tracks with the given audio session.
+ void invalidateTracksForAudioSession(audio_session_t sessionId) const {
+ Mutex::Autolock _l(mLock);
+ invalidateTracksForAudioSession_l(sessionId);
+ }
+
+ template <typename T>
+ void invalidateTracksForAudioSession_l(audio_session_t sessionId,
+ const T& tracks) const {
+ for (size_t i = 0; i < tracks.size(); ++i) {
+ const sp<TrackBase>& track = tracks[i];
+ if (sessionId == track->sessionId()) {
+ track->invalidate();
+ }
+ }
+ }
+
protected:
// entry describing an effect being suspended in mSuspendedSessions keyed vector
@@ -939,6 +959,13 @@
&& outDeviceTypes().count(mTimestampCorrectedDevice) != 0;
}
+ audio_channel_mask_t hapticChannelMask() const override {
+ return mHapticChannelMask;
+ }
+ bool supportsHapticPlayback() const {
+ return (mHapticChannelMask & AUDIO_CHANNEL_HAPTIC_ALL) != AUDIO_CHANNEL_NONE;
+ }
+
protected:
// updated by readOutputParameters_l()
size_t mNormalFrameCount; // normal mixer and effects
@@ -1061,6 +1088,11 @@
uint32_t trackCountForUid_l(uid_t uid) const;
+ void invalidateTracksForAudioSession_l(
+ audio_session_t sessionId) const override {
+ ThreadBase::invalidateTracksForAudioSession_l(sessionId, mTracks);
+ }
+
private:
friend class AudioFlinger; // for numerous
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index d366bb7..c92bce5 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -595,7 +595,10 @@
+ "_" + std::to_string(mId) + "_T");
#endif
- if (channelMask & AUDIO_CHANNEL_HAPTIC_ALL) {
+ if (thread->supportsHapticPlayback()) {
+ // If the track is attached to haptic playback thread, it is potentially to have
+ // HapticGenerator effect, which will generate haptic data, on the track. In that case,
+ // external vibration is always created for all tracks attached to haptic playback thread.
mAudioVibrationController = new AudioVibrationController(this);
mExternalVibration = new os::ExternalVibration(
mUid, "" /* pkg */, mAttr, mAudioVibrationController);
diff --git a/services/audioflinger/TypedLogger.h b/services/audioflinger/TypedLogger.h
index 6ef19bf..feb71e3 100644
--- a/services/audioflinger/TypedLogger.h
+++ b/services/audioflinger/TypedLogger.h
@@ -80,7 +80,7 @@
// TODO Permit disabling of logging at compile-time.
-// TODO A non-nullptr dummy implementation that is a nop would be faster than checking for nullptr
+// TODO A non-nullptr stub implementation that is a nop would be faster than checking for nullptr
// in the case when logging is enabled at compile-time and enabled at runtime, but it might be
// slower than nullptr check when logging is enabled at compile-time and disabled at runtime.
@@ -129,8 +129,8 @@
namespace android {
extern "C" {
-// TODO consider adding a thread_local NBLog::Writer tlDummyNBLogWriter and then
-// initialize below tlNBLogWriter to &tlDummyNBLogWriter to remove the need to
+// TODO consider adding a thread_local NBLog::Writer tlStubNBLogWriter and then
+// initialize below tlNBLogWriter to &tlStubNBLogWriter to remove the need to
// check for nullptr every time. Also reduces the need to add a new logging macro above
// each time we want to log a new type.
extern thread_local NBLog::Writer *tlNBLogWriter;
diff --git a/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h
index dd1499c..0f9bcc1 100644
--- a/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h
@@ -248,7 +248,9 @@
return String8("");
}
- std::string toString() const;
+ // Return a string to describe the DeviceVector. The sensitive information will only be
+ // added to the string if `includeSensitiveInfo` is true.
+ std::string toString(bool includeSensitiveInfo = false) const;
void dump(String8 *dst, const String8 &tag, int spaces = 0, bool verbose = true) const;
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp
index b963121..4922ebe 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp
@@ -516,7 +516,7 @@
dst->appendFormat(" Sampling rate: %d\n", mSamplingRate);
dst->appendFormat(" Format: %d\n", mFormat);
dst->appendFormat(" Channels: %08x\n", mChannelMask);
- dst->appendFormat(" Devices %s\n", mDevice->toString().c_str());
+ dst->appendFormat(" Devices %s\n", mDevice->toString(true /*includeSensitiveInfo*/).c_str());
mEnabledEffects.dump(dst, 1 /*spaces*/, false /*verbose*/);
dst->append(" AudioRecord Clients:\n");
ClientMapHandler<RecordClientDescriptor>::dump(dst);
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
index d6d472b..a2e2eec 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
@@ -245,7 +245,7 @@
dst->appendFormat(" Sampling rate: %d\n", mSamplingRate);
dst->appendFormat(" Format: %08x\n", mFormat);
dst->appendFormat(" Channels: %08x\n", mChannelMask);
- dst->appendFormat(" Devices: %s\n", devices().toString().c_str());
+ dst->appendFormat(" Devices: %s\n", devices().toString(true /*includeSensitiveInfo*/).c_str());
dst->appendFormat(" Global active count: %u\n", mGlobalActiveCount);
for (const auto &iter : mRoutingActivities) {
dst->appendFormat(" Product Strategy id: %d", iter.first);
diff --git a/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp
index a29e60e..68a32a2 100644
--- a/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp
@@ -408,7 +408,7 @@
}
}
-std::string DeviceVector::toString() const
+std::string DeviceVector::toString(bool includeSensitiveInfo) const
{
if (isEmpty()) {
return {"AUDIO_DEVICE_NONE"};
@@ -418,7 +418,7 @@
if (device != *begin()) {
result += ";";
}
- result += device->toString();
+ result += device->toString(includeSensitiveInfo);
}
return result + "}";
}
diff --git a/services/audiopolicy/engine/common/src/EngineDefaultConfig.h b/services/audiopolicy/engine/common/src/EngineDefaultConfig.h
index 981582e..1821140 100644
--- a/services/audiopolicy/engine/common/src/EngineDefaultConfig.h
+++ b/services/audiopolicy/engine/common/src/EngineDefaultConfig.h
@@ -136,7 +136,7 @@
{"rerouting",
{
{"", AUDIO_STREAM_REROUTING, "AUDIO_STREAM_REROUTING",
- {{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_UNKNOWN, AUDIO_SOURCE_DEFAULT, 0, ""}}
+ {{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_VIRTUAL_SOURCE, AUDIO_SOURCE_DEFAULT, 0, ""}}
}
},
},
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 7492cd8..f4d3d75 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -5670,7 +5670,7 @@
sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(i);
setVolumeSourceMute(ttsVolumeSource, mute/*on*/, desc, 0 /*delay*/, DeviceTypeSet());
const uint32_t latency = desc->latency() * 2;
- if (latency > maxLatency) {
+ if (desc->isActive(latency * 2) && latency > maxLatency) {
maxLatency = latency;
}
}
@@ -6157,7 +6157,9 @@
(isBtScoVolSrc && forceUseForComm != AUDIO_POLICY_FORCE_BT_SCO))) {
ALOGV("%s cannot set volume group %d volume with force use = %d for comm", __func__,
volumeSource, forceUseForComm);
- return INVALID_OPERATION;
+ // Do not return an error here as AudioService will always set both voice call
+ // and bluetooth SCO volumes due to stream aliasing.
+ return NO_ERROR;
}
if (deviceTypes.empty()) {
deviceTypes = outputDesc->devices().types();
diff --git a/services/audiopolicy/service/AudioPolicyEffects.cpp b/services/audiopolicy/service/AudioPolicyEffects.cpp
index 1ec0c5e..b738633 100644
--- a/services/audiopolicy/service/AudioPolicyEffects.cpp
+++ b/services/audiopolicy/service/AudioPolicyEffects.cpp
@@ -121,8 +121,8 @@
Vector <EffectDesc *> effects = mInputSources.valueAt(index)->mEffects;
for (size_t i = 0; i < effects.size(); i++) {
EffectDesc *effect = effects[i];
- sp<AudioEffect> fx = new AudioEffect(NULL, String16("android"), &effect->mUuid, -1, 0,
- 0, audioSession, input);
+ sp<AudioEffect> fx = new AudioEffect(String16("android"));
+ fx->set(NULL, &effect->mUuid, -1, 0, 0, audioSession, input);
status_t status = fx->initCheck();
if (status != NO_ERROR && status != ALREADY_EXISTS) {
ALOGW("addInputEffects(): failed to create Fx %s on source %d",
@@ -270,8 +270,8 @@
Vector <EffectDesc *> effects = mOutputStreams.valueAt(index)->mEffects;
for (size_t i = 0; i < effects.size(); i++) {
EffectDesc *effect = effects[i];
- sp<AudioEffect> fx = new AudioEffect(NULL, String16("android"), &effect->mUuid, 0, 0, 0,
- audioSession, output);
+ sp<AudioEffect> fx = new AudioEffect(String16("android"));
+ fx->set(NULL, &effect->mUuid, 0, 0, 0, audioSession, output);
status_t status = fx->initCheck();
if (status != NO_ERROR && status != ALREADY_EXISTS) {
ALOGE("addOutputSessionEffects(): failed to create Fx %s on session %d",
@@ -970,11 +970,11 @@
for (const auto& deviceEffectsIter : mDeviceEffects) {
const auto& deviceEffects = deviceEffectsIter.second;
for (const auto& effectDesc : deviceEffects->mEffectDescriptors->mEffects) {
- auto fx = std::make_unique<AudioEffect>(
- EFFECT_UUID_NULL, String16("android"), &effectDesc->mUuid, 0, nullptr,
- nullptr, AUDIO_SESSION_DEVICE, AUDIO_IO_HANDLE_NONE,
- AudioDeviceTypeAddr{deviceEffects->getDeviceType(),
- deviceEffects->getDeviceAddress()});
+ auto fx = std::make_unique<AudioEffect>(String16("android"));
+ fx->set(EFFECT_UUID_NULL, &effectDesc->mUuid, 0, nullptr,
+ nullptr, AUDIO_SESSION_DEVICE, AUDIO_IO_HANDLE_NONE,
+ AudioDeviceTypeAddr{deviceEffects->getDeviceType(),
+ deviceEffects->getDeviceAddress()});
status_t status = fx->initCheck();
if (status != NO_ERROR && status != ALREADY_EXISTS) {
ALOGE("%s(): failed to create Fx %s on port type=%d address=%s", __func__,
diff --git a/services/camera/libcameraservice/Android.bp b/services/camera/libcameraservice/Android.bp
index bdbcdb0..4a36865 100644
--- a/services/camera/libcameraservice/Android.bp
+++ b/services/camera/libcameraservice/Android.bp
@@ -52,7 +52,7 @@
"device3/Camera3IOStreamBase.cpp",
"device3/Camera3InputStream.cpp",
"device3/Camera3OutputStream.cpp",
- "device3/Camera3DummyStream.cpp",
+ "device3/Camera3FakeStream.cpp",
"device3/Camera3SharedOutputStream.cpp",
"device3/StatusTracker.cpp",
"device3/Camera3BufferManager.cpp",
diff --git a/services/camera/libcameraservice/api1/client2/Parameters.cpp b/services/camera/libcameraservice/api1/client2/Parameters.cpp
index dbc863b..d543cab 100644
--- a/services/camera/libcameraservice/api1/client2/Parameters.cpp
+++ b/services/camera/libcameraservice/api1/client2/Parameters.cpp
@@ -3253,6 +3253,8 @@
status_t Parameters::calculatePictureFovs(float *horizFov, float *vertFov)
const {
+ // For external camera, use FOVs = (-1.0, -1.0) as default values. Calculate
+ // FOVs only if there is sufficient information.
if (fastInfo.isExternalCamera) {
if (horizFov != NULL) {
*horizFov = -1.0;
@@ -3260,16 +3262,29 @@
if (vertFov != NULL) {
*vertFov = -1.0;
}
- return OK;
}
camera_metadata_ro_entry_t sensorSize =
staticInfo(ANDROID_SENSOR_INFO_PHYSICAL_SIZE, 2, 2);
- if (!sensorSize.count) return NO_INIT;
+ if (!sensorSize.count) {
+ // It is non-fatal for external cameras since it has default values.
+ if (fastInfo.isExternalCamera) {
+ return OK;
+ } else {
+ return NO_INIT;
+ }
+ }
camera_metadata_ro_entry_t pixelArraySize =
staticInfo(ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE, 2, 2);
- if (!pixelArraySize.count) return NO_INIT;
+ if (!pixelArraySize.count) {
+ // It is non-fatal for external cameras since it has default values.
+ if (fastInfo.isExternalCamera) {
+ return OK;
+ } else {
+ return NO_INIT;
+ }
+ }
float arrayAspect = static_cast<float>(fastInfo.arrayWidth) /
fastInfo.arrayHeight;
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.cpp b/services/camera/libcameraservice/common/CameraProviderManager.cpp
index b17d3d8..e9dcb01 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.cpp
+++ b/services/camera/libcameraservice/common/CameraProviderManager.cpp
@@ -1304,6 +1304,20 @@
}
}
+ // cameraDeviceStatusChange callbacks may be called (and causing new devices added)
+ // before setCallback returns
+ hardware::Return<Status> status = interface->setCallback(this);
+ if (!status.isOk()) {
+ ALOGE("%s: Transaction error setting up callbacks with camera provider '%s': %s",
+ __FUNCTION__, mProviderName.c_str(), status.description().c_str());
+ return DEAD_OBJECT;
+ }
+ if (status != Status::OK) {
+ ALOGE("%s: Unable to register callbacks with camera provider '%s'",
+ __FUNCTION__, mProviderName.c_str());
+ return mapToStatusT(status);
+ }
+
hardware::Return<bool> linked = interface->linkToDeath(this, /*cookie*/ mId);
if (!linked.isOk()) {
ALOGE("%s: Transaction error in linking to camera provider '%s' death: %s",
@@ -1332,7 +1346,6 @@
return res;
}
- Status status;
// Get initial list of camera devices, if any
std::vector<std::string> devices;
hardware::Return<void> ret = interface->getCameraIdList([&status, this, &devices](
@@ -1397,26 +1410,43 @@
}
}
- // cameraDeviceStatusChange callbacks may be called (and causing new devices added)
- // before setCallback returns. setCallback must be called after addDevice so that
- // the physical camera status callback can look up available regular
- // cameras.
- hardware::Return<Status> st = interface->setCallback(this);
- if (!st.isOk()) {
- ALOGE("%s: Transaction error setting up callbacks with camera provider '%s': %s",
- __FUNCTION__, mProviderName.c_str(), st.description().c_str());
- return DEAD_OBJECT;
- }
- if (st != Status::OK) {
- ALOGE("%s: Unable to register callbacks with camera provider '%s'",
- __FUNCTION__, mProviderName.c_str());
- return mapToStatusT(st);
- }
-
ALOGI("Camera provider %s ready with %zu camera devices",
mProviderName.c_str(), mDevices.size());
- mInitialized = true;
+ // Process cached status callbacks
+ std::unique_ptr<std::vector<CameraStatusInfoT>> cachedStatus =
+ std::make_unique<std::vector<CameraStatusInfoT>>();
+ {
+ std::lock_guard<std::mutex> lock(mInitLock);
+
+ for (auto& statusInfo : mCachedStatus) {
+ std::string id, physicalId;
+ status_t res = OK;
+ if (statusInfo.isPhysicalCameraStatus) {
+ res = physicalCameraDeviceStatusChangeLocked(&id, &physicalId,
+ statusInfo.cameraId, statusInfo.physicalCameraId, statusInfo.status);
+ } else {
+ res = cameraDeviceStatusChangeLocked(&id, statusInfo.cameraId, statusInfo.status);
+ }
+ if (res == OK) {
+ cachedStatus->emplace_back(statusInfo.isPhysicalCameraStatus,
+ id.c_str(), physicalId.c_str(), statusInfo.status);
+ }
+ }
+ mCachedStatus.clear();
+
+ mInitialized = true;
+ }
+
+ // The cached status change callbacks cannot be fired directly from this
+ // function, due to same-thread deadlock trying to acquire mInterfaceMutex
+ // twice.
+ if (listener != nullptr) {
+ mInitialStatusCallbackFuture = std::async(std::launch::async,
+ &CameraProviderManager::ProviderInfo::notifyInitialStatusChange, this,
+ listener, std::move(cachedStatus));
+ }
+
return OK;
}
@@ -1694,104 +1724,139 @@
CameraDeviceStatus newStatus) {
sp<StatusListener> listener;
std::string id;
- bool initialized = false;
+ std::lock_guard<std::mutex> lock(mInitLock);
+
+ if (!mInitialized) {
+ mCachedStatus.emplace_back(false /*isPhysicalCameraStatus*/,
+ cameraDeviceName.c_str(), std::string().c_str(), newStatus);
+ return hardware::Void();
+ }
+
{
std::lock_guard<std::mutex> lock(mLock);
- bool known = false;
- for (auto& deviceInfo : mDevices) {
- if (deviceInfo->mName == cameraDeviceName) {
- ALOGI("Camera device %s status is now %s, was %s", cameraDeviceName.c_str(),
- deviceStatusToString(newStatus), deviceStatusToString(deviceInfo->mStatus));
- deviceInfo->mStatus = newStatus;
- // TODO: Handle device removal (NOT_PRESENT)
- id = deviceInfo->mId;
- known = true;
- break;
- }
- }
- // Previously unseen device; status must not be NOT_PRESENT
- if (!known) {
- if (newStatus == CameraDeviceStatus::NOT_PRESENT) {
- ALOGW("Camera provider %s says an unknown camera device %s is not present. Curious.",
- mProviderName.c_str(), cameraDeviceName.c_str());
- return hardware::Void();
- }
- addDevice(cameraDeviceName, newStatus, &id);
- } else if (newStatus == CameraDeviceStatus::NOT_PRESENT) {
- removeDevice(id);
+ if (OK != cameraDeviceStatusChangeLocked(&id, cameraDeviceName, newStatus)) {
+ return hardware::Void();
}
listener = mManager->getStatusListener();
- initialized = mInitialized;
- if (reCacheConcurrentStreamingCameraIdsLocked() != OK) {
- ALOGE("%s: CameraProvider %s could not re-cache concurrent streaming camera id list ",
- __FUNCTION__, mProviderName.c_str());
- }
}
+
// Call without lock held to allow reentrancy into provider manager
- // Don't send the callback if providerInfo hasn't been initialized.
- // CameraService will initialize device status after provider is
- // initialized
- if (listener != nullptr && initialized) {
+ if (listener != nullptr) {
listener->onDeviceStatusChanged(String8(id.c_str()), newStatus);
}
+
return hardware::Void();
}
+status_t CameraProviderManager::ProviderInfo::cameraDeviceStatusChangeLocked(
+ std::string* id, const hardware::hidl_string& cameraDeviceName,
+ CameraDeviceStatus newStatus) {
+ bool known = false;
+ std::string cameraId;
+ for (auto& deviceInfo : mDevices) {
+ if (deviceInfo->mName == cameraDeviceName) {
+ ALOGI("Camera device %s status is now %s, was %s", cameraDeviceName.c_str(),
+ deviceStatusToString(newStatus), deviceStatusToString(deviceInfo->mStatus));
+ deviceInfo->mStatus = newStatus;
+ // TODO: Handle device removal (NOT_PRESENT)
+ cameraId = deviceInfo->mId;
+ known = true;
+ break;
+ }
+ }
+ // Previously unseen device; status must not be NOT_PRESENT
+ if (!known) {
+ if (newStatus == CameraDeviceStatus::NOT_PRESENT) {
+ ALOGW("Camera provider %s says an unknown camera device %s is not present. Curious.",
+ mProviderName.c_str(), cameraDeviceName.c_str());
+ return BAD_VALUE;
+ }
+ addDevice(cameraDeviceName, newStatus, &cameraId);
+ } else if (newStatus == CameraDeviceStatus::NOT_PRESENT) {
+ removeDevice(cameraId);
+ }
+ if (reCacheConcurrentStreamingCameraIdsLocked() != OK) {
+ ALOGE("%s: CameraProvider %s could not re-cache concurrent streaming camera id list ",
+ __FUNCTION__, mProviderName.c_str());
+ }
+ *id = cameraId;
+ return OK;
+}
+
hardware::Return<void> CameraProviderManager::ProviderInfo::physicalCameraDeviceStatusChange(
const hardware::hidl_string& cameraDeviceName,
const hardware::hidl_string& physicalCameraDeviceName,
CameraDeviceStatus newStatus) {
sp<StatusListener> listener;
std::string id;
- bool initialized = false;
+ std::string physicalId;
+ std::lock_guard<std::mutex> lock(mInitLock);
+
+ if (!mInitialized) {
+ mCachedStatus.emplace_back(true /*isPhysicalCameraStatus*/, cameraDeviceName,
+ physicalCameraDeviceName, newStatus);
+ return hardware::Void();
+ }
+
{
std::lock_guard<std::mutex> lock(mLock);
- bool known = false;
- for (auto& deviceInfo : mDevices) {
- if (deviceInfo->mName == cameraDeviceName) {
- id = deviceInfo->mId;
- if (!deviceInfo->mIsLogicalCamera) {
- ALOGE("%s: Invalid combination of camera id %s, physical id %s",
- __FUNCTION__, id.c_str(), physicalCameraDeviceName.c_str());
- return hardware::Void();
- }
- if (std::find(deviceInfo->mPhysicalIds.begin(), deviceInfo->mPhysicalIds.end(),
- physicalCameraDeviceName) == deviceInfo->mPhysicalIds.end()) {
- ALOGE("%s: Invalid combination of camera id %s, physical id %s",
- __FUNCTION__, id.c_str(), physicalCameraDeviceName.c_str());
- return hardware::Void();
- }
- ALOGI("Camera device %s physical device %s status is now %s, was %s",
- cameraDeviceName.c_str(), physicalCameraDeviceName.c_str(),
- deviceStatusToString(newStatus), deviceStatusToString(
- deviceInfo->mPhysicalStatus[physicalCameraDeviceName]));
- known = true;
- break;
- }
- }
- // Previously unseen device; status must not be NOT_PRESENT
- if (!known) {
- ALOGW("Camera provider %s says an unknown camera device %s-%s is not present. Curious.",
- mProviderName.c_str(), cameraDeviceName.c_str(),
- physicalCameraDeviceName.c_str());
+ if (OK != physicalCameraDeviceStatusChangeLocked(&id, &physicalId, cameraDeviceName,
+ physicalCameraDeviceName, newStatus)) {
return hardware::Void();
}
+
listener = mManager->getStatusListener();
- initialized = mInitialized;
}
// Call without lock held to allow reentrancy into provider manager
- // Don't send the callback if providerInfo hasn't been initialized.
- // CameraService will initialize device status after provider is
- // initialized
- if (listener != nullptr && initialized) {
- String8 physicalId(physicalCameraDeviceName.c_str());
+ if (listener != nullptr) {
listener->onDeviceStatusChanged(String8(id.c_str()),
- physicalId, newStatus);
+ String8(physicalId.c_str()), newStatus);
}
return hardware::Void();
}
+status_t CameraProviderManager::ProviderInfo::physicalCameraDeviceStatusChangeLocked(
+ std::string* id, std::string* physicalId,
+ const hardware::hidl_string& cameraDeviceName,
+ const hardware::hidl_string& physicalCameraDeviceName,
+ CameraDeviceStatus newStatus) {
+ bool known = false;
+ std::string cameraId;
+ for (auto& deviceInfo : mDevices) {
+ if (deviceInfo->mName == cameraDeviceName) {
+ cameraId = deviceInfo->mId;
+ if (!deviceInfo->mIsLogicalCamera) {
+ ALOGE("%s: Invalid combination of camera id %s, physical id %s",
+ __FUNCTION__, cameraId.c_str(), physicalCameraDeviceName.c_str());
+ return BAD_VALUE;
+ }
+ if (std::find(deviceInfo->mPhysicalIds.begin(), deviceInfo->mPhysicalIds.end(),
+ physicalCameraDeviceName) == deviceInfo->mPhysicalIds.end()) {
+ ALOGE("%s: Invalid combination of camera id %s, physical id %s",
+ __FUNCTION__, cameraId.c_str(), physicalCameraDeviceName.c_str());
+ return BAD_VALUE;
+ }
+ ALOGI("Camera device %s physical device %s status is now %s",
+ cameraDeviceName.c_str(), physicalCameraDeviceName.c_str(),
+ deviceStatusToString(newStatus));
+ known = true;
+ break;
+ }
+ }
+ // Previously unseen device; status must not be NOT_PRESENT
+ if (!known) {
+ ALOGW("Camera provider %s says an unknown camera device %s-%s is not present. Curious.",
+ mProviderName.c_str(), cameraDeviceName.c_str(),
+ physicalCameraDeviceName.c_str());
+ return BAD_VALUE;
+ }
+
+ *id = cameraId;
+ *physicalId = physicalCameraDeviceName.c_str();
+ return OK;
+}
+
hardware::Return<void> CameraProviderManager::ProviderInfo::torchModeStatusChange(
const hardware::hidl_string& cameraDeviceName,
TorchModeStatus newStatus) {
@@ -1946,6 +2011,20 @@
return INVALID_OPERATION;
}
+void CameraProviderManager::ProviderInfo::notifyInitialStatusChange(
+ sp<StatusListener> listener,
+ std::unique_ptr<std::vector<CameraStatusInfoT>> cachedStatus) {
+ for (auto& statusInfo : *cachedStatus) {
+ if (statusInfo.isPhysicalCameraStatus) {
+ listener->onDeviceStatusChanged(String8(statusInfo.cameraId.c_str()),
+ String8(statusInfo.physicalCameraId.c_str()), statusInfo.status);
+ } else {
+ listener->onDeviceStatusChanged(
+ String8(statusInfo.cameraId.c_str()), statusInfo.status);
+ }
+ }
+}
+
template<class DeviceInfoT>
std::unique_ptr<CameraProviderManager::ProviderInfo::DeviceInfo>
CameraProviderManager::ProviderInfo::initializeDeviceInfo(
@@ -2500,9 +2579,11 @@
CameraProviderManager::ProviderInfo::~ProviderInfo() {
+ if (mInitialStatusCallbackFuture.valid()) {
+ mInitialStatusCallbackFuture.wait();
+ }
// Destruction of ProviderInfo is only supposed to happen when the respective
// CameraProvider interface dies, so do not unregister callbacks.
-
}
status_t CameraProviderManager::mapToStatusT(const Status& s) {
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.h b/services/camera/libcameraservice/common/CameraProviderManager.h
index 20ca319..8727e7f 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.h
+++ b/services/camera/libcameraservice/common/CameraProviderManager.h
@@ -22,6 +22,7 @@
#include <unordered_set>
#include <string>
#include <mutex>
+#include <future>
#include <camera/camera2/ConcurrentCamera.h>
#include <camera/CameraParameters2.h>
@@ -398,6 +399,15 @@
const hardware::hidl_string& physicalCameraDeviceName,
hardware::camera::common::V1_0::CameraDeviceStatus newStatus) override;
+ status_t cameraDeviceStatusChangeLocked(
+ std::string* id, const hardware::hidl_string& cameraDeviceName,
+ hardware::camera::common::V1_0::CameraDeviceStatus newStatus);
+ status_t physicalCameraDeviceStatusChangeLocked(
+ std::string* id, std::string* physicalId,
+ const hardware::hidl_string& cameraDeviceName,
+ const hardware::hidl_string& physicalCameraDeviceName,
+ hardware::camera::common::V1_0::CameraDeviceStatus newStatus);
+
// hidl_death_recipient interface - this locks the parent mInterfaceMutex
virtual void serviceDied(uint64_t cookie, const wp<hidl::base::V1_0::IBase>& who) override;
@@ -439,8 +449,6 @@
const hardware::camera::common::V1_0::CameraResourceCost mResourceCost;
hardware::camera::common::V1_0::CameraDeviceStatus mStatus;
- std::map<std::string, hardware::camera::common::V1_0::CameraDeviceStatus>
- mPhysicalStatus;
wp<ProviderInfo> mParentProvider;
@@ -574,7 +582,27 @@
CameraProviderManager *mManager;
+ struct CameraStatusInfoT {
+ bool isPhysicalCameraStatus = false;
+ hardware::hidl_string cameraId;
+ hardware::hidl_string physicalCameraId;
+ hardware::camera::common::V1_0::CameraDeviceStatus status;
+ CameraStatusInfoT(bool isForPhysicalCamera, const hardware::hidl_string& id,
+ const hardware::hidl_string& physicalId,
+ hardware::camera::common::V1_0::CameraDeviceStatus s) :
+ isPhysicalCameraStatus(isForPhysicalCamera), cameraId(id),
+ physicalCameraId(physicalId), status(s) {}
+ };
+
+ // Lock to synchronize between initialize() and camera status callbacks
+ std::mutex mInitLock;
bool mInitialized = false;
+ std::vector<CameraStatusInfoT> mCachedStatus;
+ // End of scope for mInitLock
+
+ std::future<void> mInitialStatusCallbackFuture;
+ void notifyInitialStatusChange(sp<StatusListener> listener,
+ std::unique_ptr<std::vector<CameraStatusInfoT>> cachedStatus);
std::vector<std::unordered_set<std::string>> mConcurrentCameraIdCombinations;
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index 4a509aa..35c389e 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -56,7 +56,7 @@
#include "device3/Camera3Device.h"
#include "device3/Camera3OutputStream.h"
#include "device3/Camera3InputStream.h"
-#include "device3/Camera3DummyStream.h"
+#include "device3/Camera3FakeStream.h"
#include "device3/Camera3SharedOutputStream.h"
#include "CameraService.h"
#include "utils/CameraThreadState.h"
@@ -309,7 +309,7 @@
internalUpdateStatusLocked(STATUS_UNCONFIGURED);
mNextStreamId = 0;
- mDummyStreamId = NO_STREAM;
+ mFakeStreamId = NO_STREAM;
mNeedConfig = true;
mPauseStateNotify = false;
@@ -2466,12 +2466,12 @@
}
// Workaround for device HALv3.2 or older spec bug - zero streams requires
- // adding a dummy stream instead.
+ // adding a fake stream instead.
// TODO: Bug: 17321404 for fixing the HAL spec and removing this workaround.
if (mOutputStreams.size() == 0) {
- addDummyStreamLocked();
+ addFakeStreamLocked();
} else {
- tryRemoveDummyStreamLocked();
+ tryRemoveFakeStreamLocked();
}
// Start configuring the streams
@@ -2633,7 +2633,7 @@
mNeedConfig = false;
- internalUpdateStatusLocked((mDummyStreamId == NO_STREAM) ?
+ internalUpdateStatusLocked((mFakeStreamId == NO_STREAM) ?
STATUS_CONFIGURED : STATUS_UNCONFIGURED);
ALOGV("%s: Camera %s: Stream configuration complete", __FUNCTION__, mId.string());
@@ -2647,69 +2647,69 @@
return rc;
}
- if (mDummyStreamId == NO_STREAM) {
+ if (mFakeStreamId == NO_STREAM) {
mRequestBufferSM.onStreamsConfigured();
}
return OK;
}
-status_t Camera3Device::addDummyStreamLocked() {
+status_t Camera3Device::addFakeStreamLocked() {
ATRACE_CALL();
status_t res;
- if (mDummyStreamId != NO_STREAM) {
- // Should never be adding a second dummy stream when one is already
+ if (mFakeStreamId != NO_STREAM) {
+ // Should never be adding a second fake stream when one is already
// active
- SET_ERR_L("%s: Camera %s: A dummy stream already exists!",
+ SET_ERR_L("%s: Camera %s: A fake stream already exists!",
__FUNCTION__, mId.string());
return INVALID_OPERATION;
}
- ALOGV("%s: Camera %s: Adding a dummy stream", __FUNCTION__, mId.string());
+ ALOGV("%s: Camera %s: Adding a fake stream", __FUNCTION__, mId.string());
- sp<Camera3OutputStreamInterface> dummyStream =
- new Camera3DummyStream(mNextStreamId);
+ sp<Camera3OutputStreamInterface> fakeStream =
+ new Camera3FakeStream(mNextStreamId);
- res = mOutputStreams.add(mNextStreamId, dummyStream);
+ res = mOutputStreams.add(mNextStreamId, fakeStream);
if (res < 0) {
- SET_ERR_L("Can't add dummy stream to set: %s (%d)", strerror(-res), res);
+ SET_ERR_L("Can't add fake stream to set: %s (%d)", strerror(-res), res);
return res;
}
- mDummyStreamId = mNextStreamId;
+ mFakeStreamId = mNextStreamId;
mNextStreamId++;
return OK;
}
-status_t Camera3Device::tryRemoveDummyStreamLocked() {
+status_t Camera3Device::tryRemoveFakeStreamLocked() {
ATRACE_CALL();
status_t res;
- if (mDummyStreamId == NO_STREAM) return OK;
+ if (mFakeStreamId == NO_STREAM) return OK;
if (mOutputStreams.size() == 1) return OK;
- ALOGV("%s: Camera %s: Removing the dummy stream", __FUNCTION__, mId.string());
+ ALOGV("%s: Camera %s: Removing the fake stream", __FUNCTION__, mId.string());
- // Ok, have a dummy stream and there's at least one other output stream,
- // so remove the dummy
+ // Ok, have a fake stream and there's at least one other output stream,
+ // so remove the fake
- sp<Camera3StreamInterface> deletedStream = mOutputStreams.get(mDummyStreamId);
+ sp<Camera3StreamInterface> deletedStream = mOutputStreams.get(mFakeStreamId);
if (deletedStream == nullptr) {
- SET_ERR_L("Dummy stream %d does not appear to exist", mDummyStreamId);
+ SET_ERR_L("Fake stream %d does not appear to exist", mFakeStreamId);
return INVALID_OPERATION;
}
- mOutputStreams.remove(mDummyStreamId);
+ mOutputStreams.remove(mFakeStreamId);
// Free up the stream endpoint so that it can be used by some other stream
res = deletedStream->disconnect();
if (res != OK) {
- SET_ERR_L("Can't disconnect deleted dummy stream %d", mDummyStreamId);
+ SET_ERR_L("Can't disconnect deleted fake stream %d", mFakeStreamId);
// fall through since we want to still list the stream as deleted.
}
mDeletedStreams.add(deletedStream);
- mDummyStreamId = NO_STREAM;
+ mFakeStreamId = NO_STREAM;
return res;
}
@@ -2814,7 +2814,7 @@
}
void Camera3Device::checkInflightMapLengthLocked() {
- // Sanity check - if we have too many in-flight frames with long total inflight duration,
+ // Validation check - if we have too many in-flight frames with long total inflight duration,
// something has likely gone wrong. This might still be legit only if application send in
// a long burst of long exposure requests.
if (mExpectedInflightDuration > kMinWarnInflightDuration) {
@@ -4405,11 +4405,11 @@
std::set<std::string> cameraIdsWithZoom;
/**
* HAL workaround:
- * Insert a dummy trigger ID if a trigger is set but no trigger ID is
+ * Insert a fake trigger ID if a trigger is set but no trigger ID is
*/
- res = addDummyTriggerIds(captureRequest);
+ res = addFakeTriggerIds(captureRequest);
if (res != OK) {
- SET_ERR("RequestThread: Unable to insert dummy trigger IDs "
+ SET_ERR("RequestThread: Unable to insert fake trigger IDs "
"(capture request %d, HAL device: %s (%d)",
halRequest->frame_number, strerror(-res), res);
return INVALID_OPERATION;
@@ -5313,26 +5313,26 @@
return OK;
}
-status_t Camera3Device::RequestThread::addDummyTriggerIds(
+status_t Camera3Device::RequestThread::addFakeTriggerIds(
const sp<CaptureRequest> &request) {
// Trigger ID 0 had special meaning in the HAL2 spec, so avoid it here
- static const int32_t dummyTriggerId = 1;
+ static const int32_t fakeTriggerId = 1;
status_t res;
CameraMetadata &metadata = request->mSettingsList.begin()->metadata;
- // If AF trigger is active, insert a dummy AF trigger ID if none already
+ // If AF trigger is active, insert a fake AF trigger ID if none already
// exists
camera_metadata_entry afTrigger = metadata.find(ANDROID_CONTROL_AF_TRIGGER);
camera_metadata_entry afId = metadata.find(ANDROID_CONTROL_AF_TRIGGER_ID);
if (afTrigger.count > 0 &&
afTrigger.data.u8[0] != ANDROID_CONTROL_AF_TRIGGER_IDLE &&
afId.count == 0) {
- res = metadata.update(ANDROID_CONTROL_AF_TRIGGER_ID, &dummyTriggerId, 1);
+ res = metadata.update(ANDROID_CONTROL_AF_TRIGGER_ID, &fakeTriggerId, 1);
if (res != OK) return res;
}
- // If AE precapture trigger is active, insert a dummy precapture trigger ID
+ // If AE precapture trigger is active, insert a fake precapture trigger ID
// if none already exists
camera_metadata_entry pcTrigger =
metadata.find(ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER);
@@ -5341,7 +5341,7 @@
pcTrigger.data.u8[0] != ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER_IDLE &&
pcId.count == 0) {
res = metadata.update(ANDROID_CONTROL_AE_PRECAPTURE_ID,
- &dummyTriggerId, 1);
+ &fakeTriggerId, 1);
if (res != OK) return res;
}
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index 408f1f9..8e2c2f9 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -474,7 +474,7 @@
int mNextStreamId;
bool mNeedConfig;
- int mDummyStreamId;
+ int mFakeStreamId;
// Whether to send state updates upstream
// Pause when doing transparent reconfiguration
@@ -668,15 +668,15 @@
void cancelStreamsConfigurationLocked();
/**
- * Add a dummy stream to the current stream set as a workaround for
+ * Add a fake stream to the current stream set as a workaround for
* not allowing 0 streams in the camera HAL spec.
*/
- status_t addDummyStreamLocked();
+ status_t addFakeStreamLocked();
/**
- * Remove a dummy stream if the current config includes real streams.
+ * Remove a fake stream if the current config includes real streams.
*/
- status_t tryRemoveDummyStreamLocked();
+ status_t tryRemoveFakeStreamLocked();
/**
* Set device into an error state due to some fatal failure, and set an
@@ -860,7 +860,7 @@
// HAL workaround: Make sure a trigger ID always exists if
// a trigger does
- status_t addDummyTriggerIds(const sp<CaptureRequest> &request);
+ status_t addFakeTriggerIds(const sp<CaptureRequest> &request);
// Override rotate_and_crop control if needed; returns true if the current value was changed
bool overrideAutoRotateAndCrop(const sp<CaptureRequest> &request);
diff --git a/services/camera/libcameraservice/device3/Camera3DummyStream.cpp b/services/camera/libcameraservice/device3/Camera3DummyStream.cpp
deleted file mode 100644
index b637160..0000000
--- a/services/camera/libcameraservice/device3/Camera3DummyStream.cpp
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Copyright (C) 2014-2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "Camera3-DummyStream"
-#define ATRACE_TAG ATRACE_TAG_CAMERA
-//#define LOG_NDEBUG 0
-
-#include <utils/Log.h>
-#include <utils/Trace.h>
-#include "Camera3DummyStream.h"
-
-namespace android {
-
-namespace camera3 {
-
-const String8 Camera3DummyStream::DUMMY_ID;
-
-Camera3DummyStream::Camera3DummyStream(int id) :
- Camera3IOStreamBase(id, CAMERA3_STREAM_OUTPUT, DUMMY_WIDTH, DUMMY_HEIGHT,
- /*maxSize*/0, DUMMY_FORMAT, DUMMY_DATASPACE, DUMMY_ROTATION,
- DUMMY_ID) {
-
-}
-
-Camera3DummyStream::~Camera3DummyStream() {
-
-}
-
-status_t Camera3DummyStream::getBufferLocked(camera3_stream_buffer *,
- const std::vector<size_t>&) {
- ATRACE_CALL();
- ALOGE("%s: Stream %d: Dummy stream cannot produce buffers!", __FUNCTION__, mId);
- return INVALID_OPERATION;
-}
-
-status_t Camera3DummyStream::returnBufferLocked(
- const camera3_stream_buffer &,
- nsecs_t, const std::vector<size_t>&) {
- ATRACE_CALL();
- ALOGE("%s: Stream %d: Dummy stream cannot return buffers!", __FUNCTION__, mId);
- return INVALID_OPERATION;
-}
-
-status_t Camera3DummyStream::returnBufferCheckedLocked(
- const camera3_stream_buffer &,
- nsecs_t,
- bool,
- const std::vector<size_t>&,
- /*out*/
- sp<Fence>*) {
- ATRACE_CALL();
- ALOGE("%s: Stream %d: Dummy stream cannot return buffers!", __FUNCTION__, mId);
- return INVALID_OPERATION;
-}
-
-void Camera3DummyStream::dump(int fd, const Vector<String16> &args) const {
- (void) args;
- String8 lines;
- lines.appendFormat(" Stream[%d]: Dummy\n", mId);
- write(fd, lines.string(), lines.size());
-
- Camera3IOStreamBase::dump(fd, args);
-}
-
-status_t Camera3DummyStream::setTransform(int) {
- ATRACE_CALL();
- // Do nothing
- return OK;
-}
-
-status_t Camera3DummyStream::detachBuffer(sp<GraphicBuffer>* buffer, int* fenceFd) {
- (void) buffer;
- (void) fenceFd;
- // Do nothing
- return OK;
-}
-
-status_t Camera3DummyStream::configureQueueLocked() {
- // Do nothing
- return OK;
-}
-
-status_t Camera3DummyStream::disconnectLocked() {
- mState = (mState == STATE_IN_RECONFIG) ? STATE_IN_CONFIG
- : STATE_CONSTRUCTED;
- return OK;
-}
-
-status_t Camera3DummyStream::getEndpointUsage(uint64_t *usage) const {
- *usage = DUMMY_USAGE;
- return OK;
-}
-
-bool Camera3DummyStream::isVideoStream() const {
- return false;
-}
-
-bool Camera3DummyStream::isConsumerConfigurationDeferred(size_t /*surface_id*/) const {
- return false;
-}
-
-status_t Camera3DummyStream::dropBuffers(bool /*dropping*/) {
- return OK;
-}
-
-const String8& Camera3DummyStream::getPhysicalCameraId() const {
- return DUMMY_ID;
-}
-
-status_t Camera3DummyStream::setConsumers(const std::vector<sp<Surface>>& /*consumers*/) {
- ALOGE("%s: Stream %d: Dummy stream doesn't support set consumer surface!",
- __FUNCTION__, mId);
- return INVALID_OPERATION;
-}
-
-status_t Camera3DummyStream::updateStream(const std::vector<sp<Surface>> &/*outputSurfaces*/,
- const std::vector<OutputStreamInfo> &/*outputInfo*/,
- const std::vector<size_t> &/*removedSurfaceIds*/,
- KeyedVector<sp<Surface>, size_t> * /*outputMap*/) {
- ALOGE("%s: this method is not supported!", __FUNCTION__);
- return INVALID_OPERATION;
-}
-
-}; // namespace camera3
-
-}; // namespace android
diff --git a/services/camera/libcameraservice/device3/Camera3FakeStream.cpp b/services/camera/libcameraservice/device3/Camera3FakeStream.cpp
new file mode 100644
index 0000000..230512a
--- /dev/null
+++ b/services/camera/libcameraservice/device3/Camera3FakeStream.cpp
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2014-2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "Camera3-FakeStream"
+#define ATRACE_TAG ATRACE_TAG_CAMERA
+//#define LOG_NDEBUG 0
+
+#include <utils/Log.h>
+#include <utils/Trace.h>
+#include "Camera3FakeStream.h"
+
+namespace android {
+
+namespace camera3 {
+
+const String8 Camera3FakeStream::FAKE_ID;
+
+Camera3FakeStream::Camera3FakeStream(int id) :
+ Camera3IOStreamBase(id, CAMERA3_STREAM_OUTPUT, FAKE_WIDTH, FAKE_HEIGHT,
+ /*maxSize*/0, FAKE_FORMAT, FAKE_DATASPACE, FAKE_ROTATION,
+ FAKE_ID) {
+
+}
+
+Camera3FakeStream::~Camera3FakeStream() {
+
+}
+
+status_t Camera3FakeStream::getBufferLocked(camera3_stream_buffer *,
+ const std::vector<size_t>&) {
+ ATRACE_CALL();
+ ALOGE("%s: Stream %d: Fake stream cannot produce buffers!", __FUNCTION__, mId);
+ return INVALID_OPERATION;
+}
+
+status_t Camera3FakeStream::returnBufferLocked(
+ const camera3_stream_buffer &,
+ nsecs_t, const std::vector<size_t>&) {
+ ATRACE_CALL();
+ ALOGE("%s: Stream %d: Fake stream cannot return buffers!", __FUNCTION__, mId);
+ return INVALID_OPERATION;
+}
+
+status_t Camera3FakeStream::returnBufferCheckedLocked(
+ const camera3_stream_buffer &,
+ nsecs_t,
+ bool,
+ const std::vector<size_t>&,
+ /*out*/
+ sp<Fence>*) {
+ ATRACE_CALL();
+ ALOGE("%s: Stream %d: Fake stream cannot return buffers!", __FUNCTION__, mId);
+ return INVALID_OPERATION;
+}
+
+void Camera3FakeStream::dump(int fd, const Vector<String16> &args) const {
+ (void) args;
+ String8 lines;
+ lines.appendFormat(" Stream[%d]: Fake\n", mId);
+ write(fd, lines.string(), lines.size());
+
+ Camera3IOStreamBase::dump(fd, args);
+}
+
+status_t Camera3FakeStream::setTransform(int) {
+ ATRACE_CALL();
+ // Do nothing
+ return OK;
+}
+
+status_t Camera3FakeStream::detachBuffer(sp<GraphicBuffer>* buffer, int* fenceFd) {
+ (void) buffer;
+ (void) fenceFd;
+ // Do nothing
+ return OK;
+}
+
+status_t Camera3FakeStream::configureQueueLocked() {
+ // Do nothing
+ return OK;
+}
+
+status_t Camera3FakeStream::disconnectLocked() {
+ mState = (mState == STATE_IN_RECONFIG) ? STATE_IN_CONFIG
+ : STATE_CONSTRUCTED;
+ return OK;
+}
+
+status_t Camera3FakeStream::getEndpointUsage(uint64_t *usage) const {
+ *usage = FAKE_USAGE;
+ return OK;
+}
+
+bool Camera3FakeStream::isVideoStream() const {
+ return false;
+}
+
+bool Camera3FakeStream::isConsumerConfigurationDeferred(size_t /*surface_id*/) const {
+ return false;
+}
+
+status_t Camera3FakeStream::dropBuffers(bool /*dropping*/) {
+ return OK;
+}
+
+const String8& Camera3FakeStream::getPhysicalCameraId() const {
+ return FAKE_ID;
+}
+
+status_t Camera3FakeStream::setConsumers(const std::vector<sp<Surface>>& /*consumers*/) {
+ ALOGE("%s: Stream %d: Fake stream doesn't support set consumer surface!",
+ __FUNCTION__, mId);
+ return INVALID_OPERATION;
+}
+
+status_t Camera3FakeStream::updateStream(const std::vector<sp<Surface>> &/*outputSurfaces*/,
+ const std::vector<OutputStreamInfo> &/*outputInfo*/,
+ const std::vector<size_t> &/*removedSurfaceIds*/,
+ KeyedVector<sp<Surface>, size_t> * /*outputMap*/) {
+ ALOGE("%s: this method is not supported!", __FUNCTION__);
+ return INVALID_OPERATION;
+}
+
+}; // namespace camera3
+
+}; // namespace android
diff --git a/services/camera/libcameraservice/device3/Camera3DummyStream.h b/services/camera/libcameraservice/device3/Camera3FakeStream.h
similarity index 80%
rename from services/camera/libcameraservice/device3/Camera3DummyStream.h
rename to services/camera/libcameraservice/device3/Camera3FakeStream.h
index 4b67ea5..fbf37e6 100644
--- a/services/camera/libcameraservice/device3/Camera3DummyStream.h
+++ b/services/camera/libcameraservice/device3/Camera3FakeStream.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ANDROID_SERVERS_CAMERA3_DUMMY_STREAM_H
-#define ANDROID_SERVERS_CAMERA3_DUMMY_STREAM_H
+#ifndef ANDROID_SERVERS_CAMERA3_FAKE_STREAM_H
+#define ANDROID_SERVERS_CAMERA3_FAKE_STREAM_H
#include <utils/RefBase.h>
#include <gui/Surface.h>
@@ -28,23 +28,23 @@
namespace camera3 {
/**
- * A dummy output stream class, to be used as a placeholder when no valid
+ * A fake output stream class, to be used as a placeholder when no valid
* streams are configured by the client.
* This is necessary because camera HAL v3.2 or older disallow configuring
* 0 output streams, while the public camera2 API allows for it.
*/
-class Camera3DummyStream :
+class Camera3FakeStream :
public Camera3IOStreamBase,
public Camera3OutputStreamInterface {
public:
/**
- * Set up a dummy stream; doesn't actually connect to anything, and uses
- * a default dummy format and size.
+ * Set up a fake stream; doesn't actually connect to anything, and uses
+ * a default fake format and size.
*/
- explicit Camera3DummyStream(int id);
+ explicit Camera3FakeStream(int id);
- virtual ~Camera3DummyStream();
+ virtual ~Camera3FakeStream();
/**
* Camera3Stream interface
@@ -115,15 +115,15 @@
private:
- // Default dummy parameters; 320x240 is a required size for all devices,
+ // Default fake parameters; 320x240 is a required size for all devices,
// otherwise act like a SurfaceView would.
- static const int DUMMY_WIDTH = 320;
- static const int DUMMY_HEIGHT = 240;
- static const int DUMMY_FORMAT = HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED;
- static const android_dataspace DUMMY_DATASPACE = HAL_DATASPACE_UNKNOWN;
- static const camera3_stream_rotation_t DUMMY_ROTATION = CAMERA3_STREAM_ROTATION_0;
- static const uint64_t DUMMY_USAGE = GRALLOC_USAGE_HW_COMPOSER;
- static const String8 DUMMY_ID;
+ static const int FAKE_WIDTH = 320;
+ static const int FAKE_HEIGHT = 240;
+ static const int FAKE_FORMAT = HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED;
+ static const android_dataspace FAKE_DATASPACE = HAL_DATASPACE_UNKNOWN;
+ static const camera3_stream_rotation_t FAKE_ROTATION = CAMERA3_STREAM_ROTATION_0;
+ static const uint64_t FAKE_USAGE = GRALLOC_USAGE_HW_COMPOSER;
+ static const String8 FAKE_ID;
/**
* Internal Camera3Stream interface
@@ -138,7 +138,7 @@
virtual status_t getEndpointUsage(uint64_t *usage) const;
-}; // class Camera3DummyStream
+}; // class Camera3FakeStream
} // namespace camera3
diff --git a/services/camera/libcameraservice/device3/Camera3InputStream.cpp b/services/camera/libcameraservice/device3/Camera3InputStream.cpp
index cb59a76..ebd33e9 100644
--- a/services/camera/libcameraservice/device3/Camera3InputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3InputStream.cpp
@@ -27,13 +27,13 @@
namespace camera3 {
-const String8 Camera3InputStream::DUMMY_ID;
+const String8 Camera3InputStream::FAKE_ID;
Camera3InputStream::Camera3InputStream(int id,
uint32_t width, uint32_t height, int format) :
Camera3IOStreamBase(id, CAMERA3_STREAM_INPUT, width, height, /*maxSize*/0,
format, HAL_DATASPACE_UNKNOWN, CAMERA3_STREAM_ROTATION_0,
- DUMMY_ID) {
+ FAKE_ID) {
if (format == HAL_PIXEL_FORMAT_BLOB) {
ALOGE("%s: Bad format, BLOB not supported", __FUNCTION__);
diff --git a/services/camera/libcameraservice/device3/Camera3InputStream.h b/services/camera/libcameraservice/device3/Camera3InputStream.h
index 97a627a..22697b7 100644
--- a/services/camera/libcameraservice/device3/Camera3InputStream.h
+++ b/services/camera/libcameraservice/device3/Camera3InputStream.h
@@ -53,7 +53,7 @@
sp<IGraphicBufferProducer> mProducer;
Vector<BufferItem> mBuffersInFlight;
- static const String8 DUMMY_ID;
+ static const String8 FAKE_ID;
/**
* Camera3IOStreamBase
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
index 01ca006..7b812f2 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
@@ -114,7 +114,7 @@
mState = STATE_ERROR;
}
- // Sanity check for the consumer usage flag.
+ // Validation check for the consumer usage flag.
if ((consumerUsage & GraphicBuffer::USAGE_HW_TEXTURE) == 0 &&
(consumerUsage & GraphicBuffer::USAGE_HW_COMPOSER) == 0) {
ALOGE("%s: Deferred consumer usage flag is illegal %" PRIu64 "!",
diff --git a/services/camera/libcameraservice/device3/Camera3OutputUtils.cpp b/services/camera/libcameraservice/device3/Camera3OutputUtils.cpp
index 08cde5d..889ce86 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputUtils.cpp
+++ b/services/camera/libcameraservice/device3/Camera3OutputUtils.cpp
@@ -416,7 +416,7 @@
ATRACE_ASYNC_END("frame capture", frameNumber);
- // Sanity check - if sensor timestamp matches shutter timestamp in the
+ // Validation check - if sensor timestamp matches shutter timestamp in the
// case of request having callback.
if (request.hasCallback && request.requestStatus == OK &&
sensorTimestamp != shutterTimestamp) {
diff --git a/services/camera/libcameraservice/device3/DistortionMapper.cpp b/services/camera/libcameraservice/device3/DistortionMapper.cpp
index 8132225..2f388f2 100644
--- a/services/camera/libcameraservice/device3/DistortionMapper.cpp
+++ b/services/camera/libcameraservice/device3/DistortionMapper.cpp
@@ -485,7 +485,7 @@
float det = b * b - 4 * a * c;
if (det < 0) {
- // Sanity check - should not happen if pt is within the quad
+ // Validation check - should not happen if pt is within the quad
ALOGE("Bad determinant! a: %f, b: %f, c: %f, det: %f", a,b,c,det);
return -1;
}
diff --git a/services/mediametrics/AudioAnalytics.cpp b/services/mediametrics/AudioAnalytics.cpp
index 29801a4..d78d1e3 100644
--- a/services/mediametrics/AudioAnalytics.cpp
+++ b/services/mediametrics/AudioAnalytics.cpp
@@ -43,7 +43,7 @@
#endif
// Maximum length of a device name.
-static constexpr size_t STATSD_DEVICE_NAME_MAX_LENGTH = 32;
+// static constexpr size_t STATSD_DEVICE_NAME_MAX_LENGTH = 32; // unused since we suppress
// Transmit Enums to statsd in integer or strings (this must match the atoms.proto)
static constexpr bool STATSD_USE_INT_FOR_ENUM = false;
@@ -66,6 +66,8 @@
static constexpr int PREVIOUS_STATE_EXPIRE_SEC = 60 * 60; // 1 hour.
+static constexpr const char * SUPPRESSED = "SUPPRESSED";
+
/*
* For logging purposes, we list all of the MediaMetrics atom fields,
* which can then be associated with consecutive arguments to the statsd write.
@@ -448,6 +450,8 @@
std::string outputDeviceNames;
if (outputDevices.find("AUDIO_DEVICE_OUT_BLUETOOTH") != std::string::npos) {
isBluetooth = true;
+ outputDeviceNames = SUPPRESSED;
+#if 0 // TODO(b/161554630) sanitize name
mAudioAnalytics.mAnalyticsState->timeMachine().get(
"audio.device.bt_a2dp", AMEDIAMETRICS_PROP_NAME, &outputDeviceNames);
// Remove | if present
@@ -455,6 +459,7 @@
if (outputDeviceNames.size() > STATSD_DEVICE_NAME_MAX_LENGTH) {
outputDeviceNames.resize(STATSD_DEVICE_NAME_MAX_LENGTH); // truncate
}
+#endif
}
switch (itemType) {
@@ -775,7 +780,7 @@
std::lock_guard l(mLock);
mA2dpConnectionRequestNs = atNs;
++mA2dpConnectionRequests;
- mA2dpDeviceName = name;
+ mA2dpDeviceName = SUPPRESSED; // TODO(b/161554630) sanitize name
}
ALOGD("(key=%s) a2dp connection name:%s request atNs:%lld",
key.c_str(), name.c_str(), (long long)atNs);
diff --git a/services/mediametrics/AudioPowerUsage.cpp b/services/mediametrics/AudioPowerUsage.cpp
index cca6b41..33dfa8fa 100644
--- a/services/mediametrics/AudioPowerUsage.cpp
+++ b/services/mediametrics/AudioPowerUsage.cpp
@@ -200,6 +200,34 @@
return true;
}
+bool AudioPowerUsage::saveAsItems_l(
+ int32_t device, int64_t duration_ns, int32_t type, double average_vol)
+{
+ ALOGV("%s: (%#x, %d, %lld, %f)", __func__, device, type,
+ (long long)duration_ns, average_vol );
+ if (duration_ns == 0) {
+ return true; // skip duration 0 usage
+ }
+ if (device == 0) {
+ return true; //ignore unknown device
+ }
+
+ bool ret = false;
+ const int32_t input_bit = device & INPUT_DEVICE_BIT;
+ int32_t device_bits = device ^ input_bit;
+
+ while (device_bits != 0) {
+ int32_t tmp_device = device_bits & -device_bits; // get lowest bit
+ device_bits ^= tmp_device; // clear lowest bit
+ tmp_device |= input_bit; // restore input bit
+ ret = saveAsItem_l(tmp_device, duration_ns, type, average_vol);
+
+ ALOGV("%s: device %#x recorded, remaining device_bits = %#x", __func__,
+ tmp_device, device_bits);
+ }
+ return ret;
+}
+
void AudioPowerUsage::checkTrackRecord(
const std::shared_ptr<const mediametrics::Item>& item, bool isTrack)
{
@@ -245,7 +273,7 @@
ALOGV("device = %s => %d", device_strings.c_str(), device);
}
std::lock_guard l(mLock);
- saveAsItem_l(device, deviceTimeNs, type, deviceVolume);
+ saveAsItems_l(device, deviceTimeNs, type, deviceVolume);
}
void AudioPowerUsage::checkMode(const std::shared_ptr<const mediametrics::Item>& item)
@@ -262,7 +290,7 @@
if (durationNs > 0) {
mDeviceVolume = (mDeviceVolume * double(mVolumeTimeNs - mDeviceTimeNs) +
mVoiceVolume * double(endCallNs - mVolumeTimeNs)) / durationNs;
- saveAsItem_l(mPrimaryDevice, durationNs, VOICE_CALL_TYPE, mDeviceVolume);
+ saveAsItems_l(mPrimaryDevice, durationNs, VOICE_CALL_TYPE, mDeviceVolume);
}
} else if (mode == "AUDIO_MODE_IN_CALL") { // entering call mode
mStartCallNs = item->getTimestamp(); // advisory only
@@ -321,7 +349,7 @@
if (durationNs > 0) {
mDeviceVolume = (mDeviceVolume * double(mVolumeTimeNs - mDeviceTimeNs) +
mVoiceVolume * double(endDeviceNs - mVolumeTimeNs)) / durationNs;
- saveAsItem_l(mPrimaryDevice, durationNs, VOICE_CALL_TYPE, mDeviceVolume);
+ saveAsItems_l(mPrimaryDevice, durationNs, VOICE_CALL_TYPE, mDeviceVolume);
}
// reset statistics
mDeviceVolume = 0;
diff --git a/services/mediametrics/AudioPowerUsage.h b/services/mediametrics/AudioPowerUsage.h
index 446ff4f..b705a6a 100644
--- a/services/mediametrics/AudioPowerUsage.h
+++ b/services/mediametrics/AudioPowerUsage.h
@@ -85,6 +85,8 @@
REQUIRES(mLock);
static void sendItem(const std::shared_ptr<const mediametrics::Item>& item);
void collect();
+ bool saveAsItems_l(int32_t device, int64_t duration, int32_t type, double average_vol)
+ REQUIRES(mLock);
AudioAnalytics * const mAudioAnalytics;
const bool mDisabled;
diff --git a/services/mediatranscoding/MediaTranscodingService.cpp b/services/mediatranscoding/MediaTranscodingService.cpp
index 6ae630f..ef7d6d2 100644
--- a/services/mediatranscoding/MediaTranscodingService.cpp
+++ b/services/mediatranscoding/MediaTranscodingService.cpp
@@ -71,6 +71,7 @@
binder_status_t MediaTranscodingService::dump(int fd, const char** /*args*/, uint32_t /*numArgs*/) {
String8 result;
+ // TODO(b/161549994): Remove libbinder dependencies for mainline.
if (checkCallingPermission(String16("android.permission.DUMP")) == false) {
result.format(
"Permission Denial: "
diff --git a/services/mediatranscoding/tests/mediatranscodingservice_real_tests.cpp b/services/mediatranscoding/tests/mediatranscodingservice_real_tests.cpp
index 0ac03d6..1def4b9 100644
--- a/services/mediatranscoding/tests/mediatranscodingservice_real_tests.cpp
+++ b/services/mediatranscoding/tests/mediatranscodingservice_real_tests.cpp
@@ -33,7 +33,7 @@
namespace media {
-constexpr int64_t kPaddingUs = 200000;
+constexpr int64_t kPaddingUs = 400000;
constexpr int64_t kJobWithPaddingUs = 10000000 + kPaddingUs;
constexpr int32_t kBitRate = 8 * 1000 * 1000; // 8Mbs
diff --git a/services/oboeservice/AAudioServiceEndpointCapture.cpp b/services/oboeservice/AAudioServiceEndpointCapture.cpp
index 37d105b..c603e4e 100644
--- a/services/oboeservice/AAudioServiceEndpointCapture.cpp
+++ b/services/oboeservice/AAudioServiceEndpointCapture.cpp
@@ -40,17 +40,12 @@
mStreamInternal = &mStreamInternalCapture;
}
-AAudioServiceEndpointCapture::~AAudioServiceEndpointCapture() {
- delete mDistributionBuffer;
-}
-
aaudio_result_t AAudioServiceEndpointCapture::open(const aaudio::AAudioStreamRequest &request) {
aaudio_result_t result = AAudioServiceEndpointShared::open(request);
if (result == AAUDIO_OK) {
- delete mDistributionBuffer;
int distributionBufferSizeBytes = getStreamInternal()->getFramesPerBurst()
* getStreamInternal()->getBytesPerFrame();
- mDistributionBuffer = new uint8_t[distributionBufferSizeBytes];
+ mDistributionBuffer = std::make_unique<uint8_t[]>(distributionBufferSizeBytes);
}
return result;
}
@@ -67,7 +62,8 @@
int64_t mmapFramesRead = getStreamInternal()->getFramesRead();
// Read audio data from stream using a blocking read.
- result = getStreamInternal()->read(mDistributionBuffer, getFramesPerBurst(), timeoutNanos);
+ result = getStreamInternal()->read(mDistributionBuffer.get(),
+ getFramesPerBurst(), timeoutNanos);
if (result == AAUDIO_ERROR_DISCONNECTED) {
disconnectRegisteredStreams();
break;
@@ -107,7 +103,7 @@
getFramesPerBurst()) {
streamShared->incrementXRunCount();
} else {
- fifo->write(mDistributionBuffer, getFramesPerBurst());
+ fifo->write(mDistributionBuffer.get(), getFramesPerBurst());
}
clientFramesWritten = fifo->getWriteCounter();
}
diff --git a/services/oboeservice/AAudioServiceEndpointCapture.h b/services/oboeservice/AAudioServiceEndpointCapture.h
index 971da9a..ae5a189 100644
--- a/services/oboeservice/AAudioServiceEndpointCapture.h
+++ b/services/oboeservice/AAudioServiceEndpointCapture.h
@@ -17,6 +17,8 @@
#ifndef AAUDIO_SERVICE_ENDPOINT_CAPTURE_H
#define AAUDIO_SERVICE_ENDPOINT_CAPTURE_H
+#include <memory>
+
#include "client/AudioStreamInternal.h"
#include "client/AudioStreamInternalCapture.h"
@@ -28,16 +30,15 @@
class AAudioServiceEndpointCapture : public AAudioServiceEndpointShared {
public:
explicit AAudioServiceEndpointCapture(android::AAudioService &audioService);
- virtual ~AAudioServiceEndpointCapture();
+ virtual ~AAudioServiceEndpointCapture() = default;
aaudio_result_t open(const aaudio::AAudioStreamRequest &request) override;
-
void *callbackLoop() override;
private:
AudioStreamInternalCapture mStreamInternalCapture;
- uint8_t *mDistributionBuffer = nullptr;
+ std::unique_ptr<uint8_t[]> mDistributionBuffer;
};
} /* namespace aaudio */
diff --git a/services/oboeservice/AAudioServiceStreamShared.cpp b/services/oboeservice/AAudioServiceStreamShared.cpp
index 01b1c2e..f2cf016 100644
--- a/services/oboeservice/AAudioServiceStreamShared.cpp
+++ b/services/oboeservice/AAudioServiceStreamShared.cpp
@@ -105,7 +105,7 @@
}
int32_t capacityInFrames = numBursts * framesPerBurst;
- // Final sanity check.
+ // Final range check.
if (capacityInFrames > MAX_FRAMES_PER_BUFFER) {
ALOGE("calculateBufferCapacity() calc capacity %d > max %d",
capacityInFrames, MAX_FRAMES_PER_BUFFER);