Merge "CCodec: rework image data processing"
diff --git a/media/codec2/components/mpeg4_h263/C2SoftMpeg4Enc.cpp b/media/codec2/components/mpeg4_h263/C2SoftMpeg4Enc.cpp
index e1cc6b3..3c87531 100644
--- a/media/codec2/components/mpeg4_h263/C2SoftMpeg4Enc.cpp
+++ b/media/codec2/components/mpeg4_h263/C2SoftMpeg4Enc.cpp
@@ -448,6 +448,20 @@
work->worklets.front()->output.configUpdate.push_back(std::move(csd));
}
+ // handle dynamic bitrate change
+ {
+ IntfImpl::Lock lock = mIntf->lock();
+ std::shared_ptr<C2StreamBitrateInfo::output> bitrate = mIntf->getBitrate_l();
+ lock.unlock();
+
+ if (bitrate != mBitrate) {
+ mBitrate = bitrate;
+ int layerBitrate[2] = {static_cast<int>(mBitrate->value), 0};
+ ALOGV("Calling PVUpdateBitRate %d", layerBitrate[0]);
+ PVUpdateBitRate(mHandle, layerBitrate);
+ }
+ }
+
std::shared_ptr<const C2GraphicView> rView;
std::shared_ptr<C2Buffer> inputBuffer;
bool eos = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0);
diff --git a/media/codec2/sfplugin/CCodec.cpp b/media/codec2/sfplugin/CCodec.cpp
index a23b9bd..f7564f6 100644
--- a/media/codec2/sfplugin/CCodec.cpp
+++ b/media/codec2/sfplugin/CCodec.cpp
@@ -44,6 +44,7 @@
#include <media/stagefright/BufferProducerWrapper.h>
#include <media/stagefright/MediaCodecConstants.h>
#include <media/stagefright/PersistentSurface.h>
+#include <utils/NativeHandle.h>
#include "C2OMXNode.h"
#include "CCodecBufferChannel.h"
@@ -795,10 +796,30 @@
mChannel->setMetaMode(CCodecBufferChannel::MODE_ANW);
}
+ status_t err = OK;
sp<RefBase> obj;
sp<Surface> surface;
if (msg->findObject("native-window", &obj)) {
surface = static_cast<Surface *>(obj.get());
+ // setup tunneled playback
+ if (surface != nullptr) {
+ Mutexed<std::unique_ptr<Config>>::Locked configLocked(mConfig);
+ const std::unique_ptr<Config> &config = *configLocked;
+ if ((config->mDomain & Config::IS_DECODER)
+ && (config->mDomain & Config::IS_VIDEO)) {
+ int32_t tunneled;
+ if (msg->findInt32("feature-tunneled-playback", &tunneled) && tunneled != 0) {
+ ALOGI("Configuring TUNNELED video playback.");
+
+ err = configureTunneledVideoPlayback(comp, &config->mSidebandHandle, msg);
+ if (err != OK) {
+ ALOGE("configureTunneledVideoPlayback failed!");
+ return err;
+ }
+ config->mTunneled = true;
+ }
+ }
+ }
setSurface(surface);
}
@@ -1007,6 +1028,26 @@
}
}
+ /*
+ * Handle dataspace
+ */
+ int32_t usingRecorder;
+ if (msg->findInt32("android._using-recorder", &usingRecorder) && usingRecorder) {
+ android_dataspace dataSpace = HAL_DATASPACE_BT709;
+ int32_t width, height;
+ if (msg->findInt32("width", &width)
+ && msg->findInt32("height", &height)) {
+ ColorAspects aspects;
+ getColorAspectsFromFormat(msg, aspects);
+ setDefaultCodecColorAspectsIfNeeded(aspects, width, height);
+ // TODO: read dataspace / color aspect from the component
+ setColorAspectsIntoFormat(aspects, const_cast<sp<AMessage> &>(msg));
+ dataSpace = getDataSpaceForColorAspects(aspects, true /* mayexpand */);
+ }
+ msg->setInt32("android._dataspace", (int32_t)dataSpace);
+ ALOGD("setting dataspace to %x", dataSpace);
+ }
+
int32_t subscribeToAllVendorParams;
if (msg->findInt32("x-*", &subscribeToAllVendorParams) && subscribeToAllVendorParams) {
if (config->subscribeToAllVendorParams(comp, C2_MAY_BLOCK) != OK) {
@@ -1023,7 +1064,7 @@
sdkParams = msg->dup();
sdkParams->removeEntryAt(sdkParams->findEntryByName(PARAMETER_KEY_VIDEO_BITRATE));
}
- status_t err = config->getConfigUpdateFromSdkParams(
+ err = config->getConfigUpdateFromSdkParams(
comp, sdkParams, Config::IS_CONFIG, C2_DONT_BLOCK, &configUpdate);
if (err != OK) {
ALOGW("failed to convert configuration to c2 params");
@@ -1133,10 +1174,10 @@
int32_t clientPrepend;
if ((config->mDomain & Config::IS_VIDEO)
&& (config->mDomain & Config::IS_ENCODER)
- && msg->findInt32(KEY_PREPEND_HEADERS_TO_SYNC_FRAMES, &clientPrepend)
+ && msg->findInt32(KEY_PREPEND_HEADER_TO_SYNC_FRAMES, &clientPrepend)
&& clientPrepend
&& (!prepend || prepend.value != PREPEND_HEADER_TO_ALL_SYNC)) {
- ALOGE("Failed to set KEY_PREPEND_HEADERS_TO_SYNC_FRAMES");
+ ALOGE("Failed to set KEY_PREPEND_HEADER_TO_SYNC_FRAMES");
return BAD_VALUE;
}
@@ -1703,6 +1744,19 @@
}
status_t CCodec::setSurface(const sp<Surface> &surface) {
+ Mutexed<std::unique_ptr<Config>>::Locked configLocked(mConfig);
+ const std::unique_ptr<Config> &config = *configLocked;
+ if (config->mTunneled && config->mSidebandHandle != nullptr) {
+ sp<ANativeWindow> nativeWindow = static_cast<ANativeWindow *>(surface.get());
+ status_t err = native_window_set_sideband_stream(
+ nativeWindow.get(),
+ const_cast<native_handle_t *>(config->mSidebandHandle->handle()));
+ if (err != OK) {
+ ALOGE("NativeWindow(%p) native_window_set_sideband_stream(%p) failed! (err %d).",
+ nativeWindow.get(), config->mSidebandHandle->handle(), err);
+ return err;
+ }
+ }
return mChannel->setSurface(surface);
}
@@ -2099,6 +2153,51 @@
deadline->set(now + (timeout * mult), name);
}
+status_t CCodec::configureTunneledVideoPlayback(
+ std::shared_ptr<Codec2Client::Component> comp,
+ sp<NativeHandle> *sidebandHandle,
+ const sp<AMessage> &msg) {
+ std::vector<std::unique_ptr<C2SettingResult>> failures;
+
+ std::unique_ptr<C2PortTunneledModeTuning::output> tunneledPlayback =
+ C2PortTunneledModeTuning::output::AllocUnique(
+ 1,
+ C2PortTunneledModeTuning::Struct::SIDEBAND,
+ C2PortTunneledModeTuning::Struct::REALTIME,
+ 0);
+ // TODO: use KEY_AUDIO_HW_SYNC, KEY_HARDWARE_AV_SYNC_ID when they are in MediaCodecConstants.h
+ if (msg->findInt32("audio-hw-sync", &tunneledPlayback->m.syncId[0])) {
+ tunneledPlayback->m.syncType = C2PortTunneledModeTuning::Struct::sync_type_t::AUDIO_HW_SYNC;
+ } else if (msg->findInt32("hw-av-sync-id", &tunneledPlayback->m.syncId[0])) {
+ tunneledPlayback->m.syncType = C2PortTunneledModeTuning::Struct::sync_type_t::HW_AV_SYNC;
+ } else {
+ tunneledPlayback->m.syncType = C2PortTunneledModeTuning::Struct::sync_type_t::REALTIME;
+ tunneledPlayback->setFlexCount(0);
+ }
+ c2_status_t c2err = comp->config({ tunneledPlayback.get() }, C2_MAY_BLOCK, &failures);
+ if (c2err != C2_OK) {
+ return UNKNOWN_ERROR;
+ }
+
+ std::vector<std::unique_ptr<C2Param>> params;
+ c2err = comp->query({}, {C2PortTunnelHandleTuning::output::PARAM_TYPE}, C2_DONT_BLOCK, ¶ms);
+ if (c2err == C2_OK && params.size() == 1u) {
+ C2PortTunnelHandleTuning::output *videoTunnelSideband =
+ C2PortTunnelHandleTuning::output::From(params[0].get());
+ // Currently, Codec2 only supports non-fd case for sideband native_handle.
+ native_handle_t *handle = native_handle_create(0, videoTunnelSideband->flexCount());
+ *sidebandHandle = NativeHandle::create(handle, true /* ownsHandle */);
+ if (handle != nullptr && videoTunnelSideband->flexCount()) {
+ memcpy(handle->data, videoTunnelSideband->m.values,
+ sizeof(int32_t) * videoTunnelSideband->flexCount());
+ return OK;
+ } else {
+ return NO_MEMORY;
+ }
+ }
+ return UNKNOWN_ERROR;
+}
+
void CCodec::initiateReleaseIfStuck() {
std::string name;
bool pendingDeadline = false;
@@ -2111,7 +2210,9 @@
pendingDeadline = true;
}
}
- if (name.empty()) {
+ Mutexed<std::unique_ptr<Config>>::Locked configLocked(mConfig);
+ const std::unique_ptr<Config> &config = *configLocked;
+ if (config->mTunneled == false && name.empty()) {
constexpr std::chrono::steady_clock::duration kWorkDurationThreshold = 3s;
std::chrono::steady_clock::duration elapsed = mChannel->elapsed();
if (elapsed >= kWorkDurationThreshold) {
@@ -2500,4 +2601,3 @@
}
} // namespace android
-
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp
index 4d2700a..ad02edb 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.cpp
+++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp
@@ -1712,6 +1712,17 @@
}
break;
}
+ case C2PortTunnelSystemTime::CORE_INDEX: {
+ C2PortTunnelSystemTime::output frameRenderTime;
+ if (frameRenderTime.updateFrom(*param)) {
+ ALOGV("[%s] onWorkDone: frame rendered (sys:%lld ns, media:%lld us)",
+ mName, (long long)frameRenderTime.value,
+ (long long)worklet->output.ordinal.timestamp.peekll());
+ mCCodecCallback->onOutputFramesRendered(
+ worklet->output.ordinal.timestamp.peek(), frameRenderTime.value);
+ }
+ break;
+ }
default:
ALOGV("[%s] onWorkDone: unrecognized config update (%08X)",
mName, param->index());
diff --git a/media/codec2/sfplugin/CCodecConfig.cpp b/media/codec2/sfplugin/CCodecConfig.cpp
index d3814fb..f5cc98e 100644
--- a/media/codec2/sfplugin/CCodecConfig.cpp
+++ b/media/codec2/sfplugin/CCodecConfig.cpp
@@ -18,6 +18,7 @@
#define LOG_TAG "CCodecConfig"
#include <cutils/properties.h>
#include <log/log.h>
+#include <utils/NativeHandle.h>
#include <C2Component.h>
#include <C2Param.h>
@@ -321,7 +322,8 @@
CCodecConfig::CCodecConfig()
: mInputFormat(new AMessage),
mOutputFormat(new AMessage),
- mUsingSurface(false) { }
+ mUsingSurface(false),
+ mTunneled(false) { }
void CCodecConfig::initializeStandardParams() {
typedef Domain D;
@@ -507,7 +509,7 @@
add(ConfigMapper(std::string(KEY_FEATURE_) + FEATURE_SecurePlayback,
C2_PARAMKEY_SECURE_MODE, "value"));
- add(ConfigMapper(KEY_PREPEND_HEADERS_TO_SYNC_FRAMES,
+ add(ConfigMapper(KEY_PREPEND_HEADER_TO_SYNC_FRAMES,
C2_PARAMKEY_PREPEND_HEADER_MODE, "value")
.limitTo(D::ENCODER & D::VIDEO)
.withMappers([](C2Value v) -> C2Value {
@@ -531,7 +533,7 @@
return C2Value();
}));
// remove when codecs switch to PARAMKEY
- deprecated(ConfigMapper(KEY_PREPEND_HEADERS_TO_SYNC_FRAMES,
+ deprecated(ConfigMapper(KEY_PREPEND_HEADER_TO_SYNC_FRAMES,
"coding.add-csd-to-sync-frames", "value")
.limitTo(D::ENCODER & D::VIDEO));
// convert to timestamp base
diff --git a/media/codec2/sfplugin/CCodecConfig.h b/media/codec2/sfplugin/CCodecConfig.h
index 2895746..7e060f6 100644
--- a/media/codec2/sfplugin/CCodecConfig.h
+++ b/media/codec2/sfplugin/CCodecConfig.h
@@ -35,6 +35,7 @@
namespace android {
struct AMessage;
+class NativeHandle;
struct StandardParams;
/**
@@ -141,6 +142,10 @@
std::set<std::string> mLastConfig;
+ /// Tunneled codecs
+ bool mTunneled;
+ sp<NativeHandle> mSidebandHandle;
+
CCodecConfig();
/// initializes the members required to manage the format: descriptors, reflector,
diff --git a/media/codec2/sfplugin/include/media/stagefright/CCodec.h b/media/codec2/sfplugin/include/media/stagefright/CCodec.h
index dbbb5d5..ba69d7e 100644
--- a/media/codec2/sfplugin/include/media/stagefright/CCodec.h
+++ b/media/codec2/sfplugin/include/media/stagefright/CCodec.h
@@ -126,6 +126,11 @@
const std::chrono::milliseconds &timeout,
const char *name);
+ status_t configureTunneledVideoPlayback(
+ const std::shared_ptr<Codec2Client::Component> comp,
+ sp<NativeHandle> *sidebandHandle,
+ const sp<AMessage> &msg);
+
enum {
kWhatAllocate,
kWhatConfigure,
diff --git a/media/codec2/tests/vndk/C2BufferTest.cpp b/media/codec2/tests/vndk/C2BufferTest.cpp
index a9f8e17..0cfb465 100644
--- a/media/codec2/tests/vndk/C2BufferTest.cpp
+++ b/media/codec2/tests/vndk/C2BufferTest.cpp
@@ -16,11 +16,12 @@
#include <gtest/gtest.h>
-#include <C2AllocatorIon.h>
#include <C2AllocatorGralloc.h>
#include <C2Buffer.h>
#include <C2BufferPriv.h>
+#include <C2Config.h>
#include <C2ParamDef.h>
+#include <C2PlatformSupport.h>
#include <system/graphics.h>
@@ -233,10 +234,10 @@
public:
C2BufferTest()
: mBlockPoolId(C2BlockPool::PLATFORM_START),
- mLinearAllocator(std::make_shared<C2AllocatorIon>('i')),
mSize(0u),
mAddr(nullptr),
mGraphicAllocator(std::make_shared<C2AllocatorGralloc>('g')) {
+ getLinearAllocator(&mLinearAllocator);
}
~C2BufferTest() = default;
@@ -329,6 +330,11 @@
}
private:
+ void getLinearAllocator(std::shared_ptr<C2Allocator>* mLinearAllocator) {
+ std::shared_ptr<C2AllocatorStore> store = android::GetCodec2PlatformAllocatorStore();
+ ASSERT_EQ(store->fetchAllocator(C2AllocatorStore::DEFAULT_LINEAR, mLinearAllocator), C2_OK);
+ }
+
C2BlockPool::local_id_t mBlockPoolId;
std::shared_ptr<C2Allocator> mLinearAllocator;
std::shared_ptr<C2LinearAllocation> mLinearAllocation;
diff --git a/media/codecs/m4v_h263/enc/src/mp4enc_api.cpp b/media/codecs/m4v_h263/enc/src/mp4enc_api.cpp
index d43156c..4555203 100644
--- a/media/codecs/m4v_h263/enc/src/mp4enc_api.cpp
+++ b/media/codecs/m4v_h263/enc/src/mp4enc_api.cpp
@@ -524,11 +524,9 @@
}
/* check bit rate */
- /* set max bit rate */
for (i = 0; i < encParams->nLayers; i++)
{
encParams->LayerBitRate[i] = encOption->bitRate[i];
- encParams->LayerMaxBitRate[i] = encOption->bitRate[i];
}
if (encParams->nLayers > 1)
{
@@ -3302,6 +3300,3 @@
}
#endif /* #ifndef ORIGINAL_VERSION */
-
-
-
diff --git a/media/libeffects/lvm/lib/Android.bp b/media/libeffects/lvm/lib/Android.bp
index a044295..5d75055 100644
--- a/media/libeffects/lvm/lib/Android.bp
+++ b/media/libeffects/lvm/lib/Android.bp
@@ -59,7 +59,6 @@
"Eq/src/LVEQNB_Init.cpp",
"Eq/src/LVEQNB_Process.cpp",
"Eq/src/LVEQNB_Tables.cpp",
- "Common/src/InstAlloc.cpp",
"Common/src/DC_2I_D16_TRC_WRA_01.cpp",
"Common/src/DC_2I_D16_TRC_WRA_01_Init.cpp",
"Common/src/Copy_16.cpp",
@@ -146,11 +145,9 @@
"Reverb/src/LVREV_ClearAudioBuffers.cpp",
"Reverb/src/LVREV_GetControlParameters.cpp",
"Reverb/src/LVREV_GetInstanceHandle.cpp",
- "Reverb/src/LVREV_GetMemoryTable.cpp",
"Reverb/src/LVREV_Process.cpp",
"Reverb/src/LVREV_SetControlParameters.cpp",
"Reverb/src/LVREV_Tables.cpp",
- "Common/src/InstAlloc.cpp",
"Common/src/LoadConst_32.cpp",
"Common/src/From2iToMono_32.cpp",
"Common/src/Mult3s_32x16.cpp",
diff --git a/media/libeffects/lvm/lib/Bass/src/LVDBE_Control.cpp b/media/libeffects/lvm/lib/Bass/src/LVDBE_Control.cpp
index 3fc9e95..9fe8116 100644
--- a/media/libeffects/lvm/lib/Bass/src/LVDBE_Control.cpp
+++ b/media/libeffects/lvm/lib/Bass/src/LVDBE_Control.cpp
@@ -277,13 +277,15 @@
/*
* Create biquad instance
*/
- pInstance->pHPFBiquad.reset(
- new android::audio_utils::BiquadFilter<LVM_FLOAT>(pParams->NrChannels));
-
+ if (pInstance->Params.NrChannels != pParams->NrChannels) {
+ pInstance->pHPFBiquad.reset(
+ new android::audio_utils::BiquadFilter<LVM_FLOAT>(pParams->NrChannels));
+ }
/*
* Update the filters
*/
if ((pInstance->Params.SampleRate != pParams->SampleRate) ||
+ (pInstance->Params.NrChannels != pParams->NrChannels) ||
(pInstance->Params.CentreFrequency != pParams->CentreFrequency)) {
LVDBE_SetFilters(pInstance, /* Instance pointer */
pParams); /* New parameters */
diff --git a/media/libeffects/lvm/lib/Bass/src/LVDBE_Init.cpp b/media/libeffects/lvm/lib/Bass/src/LVDBE_Init.cpp
index bbe7de0..b113f48 100644
--- a/media/libeffects/lvm/lib/Bass/src/LVDBE_Init.cpp
+++ b/media/libeffects/lvm/lib/Bass/src/LVDBE_Init.cpp
@@ -79,6 +79,7 @@
pInstance->Params.SampleRate = LVDBE_FS_8000;
pInstance->Params.VolumeControl = LVDBE_VOLUME_OFF;
pInstance->Params.VolumedB = 0;
+ pInstance->Params.NrChannels = FCC_2;
/*
* Create pointer to data and coef memory
@@ -91,7 +92,7 @@
* Create biquad instance
*/
pInstance->pHPFBiquad.reset(
- new android::audio_utils::BiquadFilter<LVM_FLOAT>(LVM_MAX_CHANNELS));
+ new android::audio_utils::BiquadFilter<LVM_FLOAT>(pInstance->Params.NrChannels));
pInstance->pBPFBiquad.reset(new android::audio_utils::BiquadFilter<LVM_FLOAT>(FCC_1));
/*
diff --git a/media/libeffects/lvm/lib/Bundle/src/LVM_Init.cpp b/media/libeffects/lvm/lib/Bundle/src/LVM_Init.cpp
index 6ea08bc..9f5f448 100644
--- a/media/libeffects/lvm/lib/Bundle/src/LVM_Init.cpp
+++ b/media/libeffects/lvm/lib/Bundle/src/LVM_Init.cpp
@@ -25,7 +25,6 @@
#include "LVM_Private.h"
#include "LVM_Tables.h"
#include "VectorArithmetic.h"
-#include "InstAlloc.h"
/****************************************************************************************/
/* */
diff --git a/media/libeffects/lvm/lib/Common/lib/InstAlloc.h b/media/libeffects/lvm/lib/Common/lib/InstAlloc.h
deleted file mode 100644
index 17699ef..0000000
--- a/media/libeffects/lvm/lib/Common/lib/InstAlloc.h
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 2004-2010 NXP Software
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __INSTALLOC_H__
-#define __INSTALLOC_H__
-
-#include "LVM_Types.h"
-/*######################################################################################*/
-/* Type declarations */
-/*######################################################################################*/
-typedef struct {
- LVM_UINT32 TotalSize; /* Accumulative total memory size */
- uintptr_t pNextMember; /* Pointer to the next instance member to be allocated */
-} INST_ALLOC;
-
-/*######################################################################################*/
-/* Function prototypes */
-/*######################################################################################*/
-
-/****************************************************************************************
- * Name : InstAlloc_Init()
- * Input : pms - Pointer to the INST_ALLOC instance
- StartAddr - Base address of the instance memory
- * Returns : Error code
- * Description : Initializes the instance distribution and memory size calculation function
- * Remarks :
- ****************************************************************************************/
-
-void InstAlloc_Init(INST_ALLOC* pms, void* StartAddr);
-
-/****************************************************************************************
- * Name : InstAlloc_AddMember()
- * Input : pms - Pointer to the INST_ALLOC instance
- Size - The size in bytes of the new added member
- * Returns : A pointer to the new added member
- * Description : Allocates space for a new member in the instance memory and returns
- a pointer to this new member. The start address of all members will
- be 32 bit alligned.
- * Remarks :
- ****************************************************************************************/
-
-void* InstAlloc_AddMember(INST_ALLOC* pms, LVM_UINT32 Size);
-
-/****************************************************************************************
- * Name : InstAlloc_GetTotal()
- * Input : pms - Pointer to the INST_ALLOC instance
- * Returns : The instance memory size
- * Description : This functions returns the calculated instance memory size
- * Remarks :
- ****************************************************************************************/
-
-LVM_UINT32 InstAlloc_GetTotal(INST_ALLOC* pms);
-
-void* InstAlloc_AddMemberAllRet(INST_ALLOC* pms, LVM_UINT32 Size[], void** ptr);
-
-void* InstAlloc_AddMemberAll(INST_ALLOC* pms, LVM_UINT32 Size[], LVM_MemoryTable_st* pMemoryTable);
-
-void InstAlloc_InitAll(INST_ALLOC* pms, LVM_MemoryTable_st* pMemoryTable);
-
-void InstAlloc_InitAll_NULL(INST_ALLOC* pms);
-
-#endif /* __JBS_INSTALLOC_H__ */
diff --git a/media/libeffects/lvm/lib/Common/lib/LVM_Types.h b/media/libeffects/lvm/lib/Common/lib/LVM_Types.h
index 5cdcf35..7cfaf27 100644
--- a/media/libeffects/lvm/lib/Common/lib/LVM_Types.h
+++ b/media/libeffects/lvm/lib/Common/lib/LVM_Types.h
@@ -134,27 +134,6 @@
return LVM_FS_INVALID;
}
-/* Memory Types */
-typedef enum {
- LVM_PERSISTENT_SLOW_DATA = LVM_MEMREGION_PERSISTENT_SLOW_DATA,
- LVM_PERSISTENT_FAST_DATA = LVM_MEMREGION_PERSISTENT_FAST_DATA,
- LVM_PERSISTENT_FAST_COEF = LVM_MEMREGION_PERSISTENT_FAST_COEF,
- LVM_TEMPORARY_FAST = LVM_MEMREGION_TEMPORARY_FAST,
- LVM_MEMORYTYPE_DUMMY = LVM_MAXENUM
-} LVM_MemoryTypes_en;
-
-/* Memory region definition */
-typedef struct {
- LVM_UINT32 Size; /* Region size in bytes */
- LVM_MemoryTypes_en Type; /* Region type */
- void* pBaseAddress; /* Pointer to the region base address */
-} LVM_MemoryRegion_st;
-
-/* Memory table containing the region definitions */
-typedef struct {
- LVM_MemoryRegion_st Region[LVM_NR_MEMORY_REGIONS]; /* One definition for each region */
-} LVM_MemoryTable_st;
-
/****************************************************************************************/
/* */
/* Standard Function Prototypes */
diff --git a/media/libeffects/lvm/lib/Common/src/InstAlloc.cpp b/media/libeffects/lvm/lib/Common/src/InstAlloc.cpp
deleted file mode 100644
index 2cfe056..0000000
--- a/media/libeffects/lvm/lib/Common/src/InstAlloc.cpp
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * Copyright (C) 2004-2010 NXP Software
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "InstAlloc.h"
-
-/****************************************************************************************
- * Name : InstAlloc_Init()
- * Input : pms - Pointer to the INST_ALLOC instance
- StartAddr - Base address of the instance memory
- * Returns : Error code
- * Description : Initializes the instance distribution and memory size calculation function
- * Remarks :
- ****************************************************************************************/
-
-void InstAlloc_Init(INST_ALLOC* pms, void* StartAddr) {
- pms->TotalSize = 3;
- pms->pNextMember = (((uintptr_t)StartAddr + 3) & (uintptr_t)~3);
-}
-
-/****************************************************************************************
- * Name : InstAlloc_AddMember()
- * Input : pms - Pointer to the INST_ALLOC instance
- Size - The size in bytes of the new added member
- * Returns : A pointer to the new added member
- * Description : Allocates space for a new member in the instance memory and returns
- a pointer to this new member. The start address of all members will
- be 32 bit alligned.
- * Remarks :
- ****************************************************************************************/
-
-void* InstAlloc_AddMember(INST_ALLOC* pms, LVM_UINT32 Size) {
- void* NewMemberAddress; /* Variable to temporarily store the return value */
- NewMemberAddress = (void*)pms->pNextMember;
-
- Size = ((Size + 3) & (LVM_UINT32)~3); /* Ceil the size to a multiple of four */
-
- pms->TotalSize += Size;
- pms->pNextMember += Size;
-
- return (NewMemberAddress);
-}
-
-/****************************************************************************************
- * Name : InstAlloc_GetTotal()
- * Input : pms - Pointer to the INST_ALLOC instance
- * Returns : The instance memory size
- * Description : This functions returns the calculated instance memory size
- * Remarks :
- ****************************************************************************************/
-
-LVM_UINT32 InstAlloc_GetTotal(INST_ALLOC* pms) {
- if (pms->TotalSize > 3) {
- return (pms->TotalSize);
- } else {
- return 0; /* No memory added */
- }
-}
-
-void InstAlloc_InitAll(INST_ALLOC* pms, LVM_MemoryTable_st* pMemoryTable) {
- uintptr_t StartAddr;
-
- StartAddr = (uintptr_t)pMemoryTable->Region[LVM_PERSISTENT_SLOW_DATA].pBaseAddress;
-
- pms[0].TotalSize = 3;
- pms[0].pNextMember = ((StartAddr + 3) & (uintptr_t)~3);
-
- StartAddr = (uintptr_t)pMemoryTable->Region[LVM_PERSISTENT_FAST_DATA].pBaseAddress;
-
- pms[1].TotalSize = 3;
- pms[1].pNextMember = ((StartAddr + 3) & (uintptr_t)~3);
-
- StartAddr = (uintptr_t)pMemoryTable->Region[LVM_PERSISTENT_FAST_COEF].pBaseAddress;
-
- pms[2].TotalSize = 3;
- pms[2].pNextMember = ((StartAddr + 3) & (uintptr_t)~3);
-
- StartAddr = (uintptr_t)pMemoryTable->Region[LVM_TEMPORARY_FAST].pBaseAddress;
-
- pms[3].TotalSize = 3;
- pms[3].pNextMember = ((StartAddr + 3) & (uintptr_t)~3);
-}
-
-/****************************************************************************************
- * Name : InstAlloc_InitAll_NULL()
- * Input : pms - Pointer to array of four INST_ALLOC instances
- * Returns : Nothing
- * Description : This function reserves Size of 3 bytes for all memory regions and
- * intializes pNextMember for all regions to 0
- * Remarks :
- ****************************************************************************************/
-
-void InstAlloc_InitAll_NULL(INST_ALLOC* pms) {
- pms[0].TotalSize = 3;
- pms[0].pNextMember = 0;
-
- pms[1].TotalSize = 3;
- pms[1].pNextMember = 0;
-
- pms[2].TotalSize = 3;
- pms[2].pNextMember = 0;
-
- pms[3].TotalSize = 3;
- pms[3].pNextMember = 0;
-}
-
-void* InstAlloc_AddMemberAll(INST_ALLOC* pms, LVM_UINT32 Size[], LVM_MemoryTable_st* pMemoryTable) {
- void* NewMemberAddress; /* Variable to temporarily store the return value */
-
- /* coverity[returned_pointer] Ignore coverity warning that ptr is not used */
- NewMemberAddress =
- InstAlloc_AddMember(&pms[LVM_PERSISTENT_SLOW_DATA], Size[LVM_PERSISTENT_SLOW_DATA]);
-
- pMemoryTable->Region[LVM_PERSISTENT_SLOW_DATA].Size =
- InstAlloc_GetTotal(&pms[LVM_PERSISTENT_SLOW_DATA]);
- pMemoryTable->Region[LVM_PERSISTENT_SLOW_DATA].Type = LVM_PERSISTENT_SLOW_DATA;
- pMemoryTable->Region[LVM_PERSISTENT_SLOW_DATA].pBaseAddress = LVM_NULL;
-
- NewMemberAddress =
- InstAlloc_AddMember(&pms[LVM_PERSISTENT_FAST_DATA], Size[LVM_PERSISTENT_FAST_DATA]);
-
- pMemoryTable->Region[LVM_PERSISTENT_FAST_DATA].Size =
- InstAlloc_GetTotal(&pms[LVM_PERSISTENT_FAST_DATA]);
- pMemoryTable->Region[LVM_PERSISTENT_FAST_DATA].Type = LVM_PERSISTENT_FAST_DATA;
- pMemoryTable->Region[LVM_PERSISTENT_FAST_DATA].pBaseAddress = LVM_NULL;
-
- NewMemberAddress =
- InstAlloc_AddMember(&pms[LVM_PERSISTENT_FAST_COEF], Size[LVM_PERSISTENT_FAST_COEF]);
-
- pMemoryTable->Region[LVM_PERSISTENT_FAST_COEF].Size =
- InstAlloc_GetTotal(&pms[LVM_PERSISTENT_FAST_COEF]);
- pMemoryTable->Region[LVM_PERSISTENT_FAST_COEF].Type = LVM_PERSISTENT_FAST_COEF;
- pMemoryTable->Region[LVM_PERSISTENT_FAST_COEF].pBaseAddress = LVM_NULL;
-
- NewMemberAddress = InstAlloc_AddMember(&pms[LVM_TEMPORARY_FAST], Size[LVM_TEMPORARY_FAST]);
-
- pMemoryTable->Region[LVM_TEMPORARY_FAST].Size = InstAlloc_GetTotal(&pms[LVM_TEMPORARY_FAST]);
- pMemoryTable->Region[LVM_TEMPORARY_FAST].Type = LVM_TEMPORARY_FAST;
- pMemoryTable->Region[LVM_TEMPORARY_FAST].pBaseAddress = LVM_NULL;
-
- return (NewMemberAddress);
-}
-
-void* InstAlloc_AddMemberAllRet(INST_ALLOC* pms, LVM_UINT32 Size[], void** ptr) {
- ptr[0] = InstAlloc_AddMember(&pms[LVM_PERSISTENT_SLOW_DATA], Size[LVM_PERSISTENT_SLOW_DATA]);
- ptr[1] = InstAlloc_AddMember(&pms[LVM_PERSISTENT_FAST_DATA], Size[LVM_PERSISTENT_FAST_DATA]);
- ptr[2] = InstAlloc_AddMember(&pms[LVM_PERSISTENT_FAST_COEF], Size[LVM_PERSISTENT_FAST_COEF]);
- ptr[3] = InstAlloc_AddMember(&pms[LVM_TEMPORARY_FAST], Size[LVM_TEMPORARY_FAST]);
-
- return (ptr[0]);
-}
diff --git a/media/libeffects/lvm/lib/Eq/src/LVEQNB_Init.cpp b/media/libeffects/lvm/lib/Eq/src/LVEQNB_Init.cpp
index 8cb7013..3473262 100644
--- a/media/libeffects/lvm/lib/Eq/src/LVEQNB_Init.cpp
+++ b/media/libeffects/lvm/lib/Eq/src/LVEQNB_Init.cpp
@@ -24,7 +24,6 @@
#include <stdlib.h>
#include "LVEQNB.h"
#include "LVEQNB_Private.h"
-#include "InstAlloc.h"
#include <string.h> /* For memset */
/****************************************************************************************/
diff --git a/media/libeffects/lvm/lib/Reverb/lib/LVREV.h b/media/libeffects/lvm/lib/Reverb/lib/LVREV.h
index 489bc6f..82e94da 100644
--- a/media/libeffects/lvm/lib/Reverb/lib/LVREV.h
+++ b/media/libeffects/lvm/lib/Reverb/lib/LVREV.h
@@ -78,11 +78,6 @@
/* */
/****************************************************************************************/
-/* Memory table containing the region definitions */
-typedef struct {
- LVM_MemoryRegion_st Region[LVREV_NR_MEMORY_REGIONS]; /* One definition for each region */
-} LVREV_MemoryTable_st;
-
/* Control Parameter structure */
typedef struct {
/* General parameters */
@@ -121,46 +116,6 @@
/****************************************************************************************/
/* */
-/* FUNCTION: LVREV_GetMemoryTable */
-/* */
-/* DESCRIPTION: */
-/* This function is used to obtain the LVREV module memory requirements to support */
-/* memory allocation. It can also be used to return the memory base address provided */
-/* during memory allocation to support freeing of memory when the LVREV module is no */
-/* longer required. It is called in two ways: */
-/* */
-/* hInstance = NULL Returns the memory requirements */
-/* hInstance = Instance handle Returns the memory requirements and allocated */
-/* base addresses. */
-/* */
-/* When this function is called with hInstance = NULL the memory base address pointers */
-/* will be NULL on return. */
-/* */
-/* When the function is called for freeing memory, hInstance = Instance Handle the */
-/* memory table returns the allocated memory and base addresses used during */
-/* initialisation. */
-/* */
-/* PARAMETERS: */
-/* hInstance Instance Handle */
-/* pMemoryTable Pointer to an empty memory table */
-/* pInstanceParams Pointer to the instance parameters */
-/* */
-/* RETURNS: */
-/* LVREV_SUCCESS Succeeded */
-/* LVREV_NULLADDRESS When pMemoryTable is NULL */
-/* LVREV_NULLADDRESS When requesting memory requirements and pInstanceParams */
-/* is NULL */
-/* */
-/* NOTES: */
-/* 1. This function may be interrupted by the LVREV_Process function */
-/* */
-/****************************************************************************************/
-LVREV_ReturnStatus_en LVREV_GetMemoryTable(LVREV_Handle_t hInstance,
- LVREV_MemoryTable_st* pMemoryTable,
- LVREV_InstanceParams_st* pInstanceParams);
-
-/****************************************************************************************/
-/* */
/* FUNCTION: LVREV_GetInstanceHandle */
/* */
/* DESCRIPTION: */
@@ -174,7 +129,6 @@
/* */
/* PARAMETERS: */
/* phInstance Pointer to the instance handle */
-/* pMemoryTable Pointer to the memory definition table */
/* pInstanceParams Pointer to the instance parameters */
/* */
/* RETURNS: */
@@ -186,7 +140,6 @@
/* */
/****************************************************************************************/
LVREV_ReturnStatus_en LVREV_GetInstanceHandle(LVREV_Handle_t* phInstance,
- LVREV_MemoryTable_st* pMemoryTable,
LVREV_InstanceParams_st* pInstanceParams);
/****************************************************************************************/
diff --git a/media/libeffects/lvm/lib/Reverb/src/LVREV_GetInstanceHandle.cpp b/media/libeffects/lvm/lib/Reverb/src/LVREV_GetInstanceHandle.cpp
index 298655b..3a63698 100644
--- a/media/libeffects/lvm/lib/Reverb/src/LVREV_GetInstanceHandle.cpp
+++ b/media/libeffects/lvm/lib/Reverb/src/LVREV_GetInstanceHandle.cpp
@@ -21,7 +21,6 @@
/* */
/****************************************************************************************/
#include "LVREV_Private.h"
-#include "InstAlloc.h"
/****************************************************************************************/
/* */
@@ -34,7 +33,6 @@
/* */
/* PARAMETERS: */
/* phInstance pointer to the instance handle */
-/* pMemoryTable Pointer to the memory definition table */
/* pInstanceParams Pointer to the instance parameters */
/* */
/* RETURNS: */
@@ -46,12 +44,7 @@
/* */
/****************************************************************************************/
LVREV_ReturnStatus_en LVREV_GetInstanceHandle(LVREV_Handle_t* phInstance,
- LVREV_MemoryTable_st* pMemoryTable,
LVREV_InstanceParams_st* pInstanceParams) {
- INST_ALLOC SlowData;
- INST_ALLOC FastData;
- INST_ALLOC FastCoef;
- INST_ALLOC Temporary;
LVREV_Instance_st* pLVREV_Private;
LVM_INT16 i;
LVM_UINT16 MaxBlockSize;
@@ -60,18 +53,9 @@
* Check for error conditions
*/
/* Check for NULL pointers */
- if ((phInstance == LVM_NULL) || (pMemoryTable == LVM_NULL) || (pInstanceParams == LVM_NULL)) {
+ if ((phInstance == LVM_NULL) || (pInstanceParams == LVM_NULL)) {
return LVREV_NULLADDRESS;
}
- /* Check the memory table for NULL pointers */
- for (i = 0; i < LVREV_NR_MEMORY_REGIONS; i++) {
- if (pMemoryTable->Region[i].Size != 0) {
- if (pMemoryTable->Region[i].pBaseAddress == LVM_NULL) {
- return (LVREV_NULLADDRESS);
- }
- }
- }
-
/*
* Check all instance parameters are in range
*/
@@ -88,36 +72,12 @@
}
/*
- * Initialise the InstAlloc instances
- */
- InstAlloc_Init(&SlowData, pMemoryTable->Region[LVM_PERSISTENT_SLOW_DATA].pBaseAddress);
- InstAlloc_Init(&FastData, pMemoryTable->Region[LVM_PERSISTENT_FAST_DATA].pBaseAddress);
- InstAlloc_Init(&FastCoef, pMemoryTable->Region[LVM_PERSISTENT_FAST_COEF].pBaseAddress);
- InstAlloc_Init(&Temporary, pMemoryTable->Region[LVM_TEMPORARY_FAST].pBaseAddress);
-
- /*
- * Zero all memory regions
- */
- LoadConst_Float(
- 0, (LVM_FLOAT*)pMemoryTable->Region[LVM_PERSISTENT_SLOW_DATA].pBaseAddress,
- (LVM_INT16)((pMemoryTable->Region[LVM_PERSISTENT_SLOW_DATA].Size) / sizeof(LVM_FLOAT)));
- LoadConst_Float(
- 0, (LVM_FLOAT*)pMemoryTable->Region[LVM_PERSISTENT_FAST_DATA].pBaseAddress,
- (LVM_INT16)((pMemoryTable->Region[LVM_PERSISTENT_FAST_DATA].Size) / sizeof(LVM_FLOAT)));
- LoadConst_Float(
- 0, (LVM_FLOAT*)pMemoryTable->Region[LVM_PERSISTENT_FAST_COEF].pBaseAddress,
- (LVM_INT16)((pMemoryTable->Region[LVM_PERSISTENT_FAST_COEF].Size) / sizeof(LVM_FLOAT)));
- LoadConst_Float(
- 0, (LVM_FLOAT*)pMemoryTable->Region[LVM_TEMPORARY_FAST].pBaseAddress,
- (LVM_INT16)((pMemoryTable->Region[LVM_TEMPORARY_FAST].Size) / sizeof(LVM_FLOAT)));
- /*
* Set the instance handle if not already initialised
*/
if (*phInstance == LVM_NULL) {
*phInstance = new LVREV_Instance_st{};
}
pLVREV_Private = (LVREV_Instance_st*)*phInstance;
- pLVREV_Private->MemoryTable = *pMemoryTable;
if (pInstanceParams->NumDelays == LVREV_DELAYLINES_4) {
MaxBlockSize = LVREV_MAX_AP_DELAY[3];
@@ -135,12 +95,9 @@
* Set the data, coefficient and temporary memory pointers
*/
for (size_t i = 0; i < pInstanceParams->NumDelays; i++) {
- pLVREV_Private->pDelay_T[i] = (LVM_FLOAT*)InstAlloc_AddMember(
- &FastData, LVREV_MAX_T_DELAY[i] * sizeof(LVM_FLOAT));
+ pLVREV_Private->pDelay_T[i] = (LVM_FLOAT*)calloc(LVREV_MAX_T_DELAY[i], sizeof(LVM_FLOAT));
/* Scratch for each delay line output */
- pLVREV_Private->pScratchDelayLine[i] =
- (LVM_FLOAT*)InstAlloc_AddMember(&Temporary, sizeof(LVM_FLOAT) * MaxBlockSize);
- LoadConst_Float(0, pLVREV_Private->pDelay_T[i], LVREV_MAX_T_DELAY[i]);
+ pLVREV_Private->pScratchDelayLine[i] = (LVM_FLOAT*)calloc(MaxBlockSize, sizeof(LVM_FLOAT));
}
/* All-pass delay buffer addresses and sizes */
for (size_t i = 0; i < LVREV_DELAYLINES_4; i++) {
@@ -149,12 +106,9 @@
pLVREV_Private->AB_Selection = 1; /* Select smoothing A to B */
/* General purpose scratch */
- pLVREV_Private->pScratch =
- (LVM_FLOAT*)InstAlloc_AddMember(&Temporary, sizeof(LVM_FLOAT) * MaxBlockSize);
+ pLVREV_Private->pScratch = (LVM_FLOAT*)calloc(MaxBlockSize, sizeof(LVM_FLOAT));
/* Mono->stereo input save for end mix */
- pLVREV_Private->pInputSave =
- (LVM_FLOAT*)InstAlloc_AddMember(&Temporary, 2 * sizeof(LVM_FLOAT) * MaxBlockSize);
- LoadConst_Float(0, pLVREV_Private->pInputSave, (LVM_INT16)(MaxBlockSize * 2));
+ pLVREV_Private->pInputSave = (LVM_FLOAT*)calloc(FCC_2 * MaxBlockSize, sizeof(LVM_FLOAT));
/*
* Save the instance parameters in the instance structure
@@ -289,7 +243,28 @@
return LVREV_NULLADDRESS;
}
- delete (LVREV_Instance_st*)hInstance;
+ LVREV_Instance_st* pLVREV_Private = (LVREV_Instance_st*)hInstance;
+
+ for (size_t i = 0; i < pLVREV_Private->InstanceParams.NumDelays; i++) {
+ if (pLVREV_Private->pDelay_T[i]) {
+ free(pLVREV_Private->pDelay_T[i]);
+ pLVREV_Private->pDelay_T[i] = LVM_NULL;
+ }
+ if (pLVREV_Private->pScratchDelayLine[i]) {
+ free(pLVREV_Private->pScratchDelayLine[i]);
+ pLVREV_Private->pScratchDelayLine[i] = LVM_NULL;
+ }
+ }
+ if (pLVREV_Private->pScratch) {
+ free(pLVREV_Private->pScratch);
+ pLVREV_Private->pScratch = LVM_NULL;
+ }
+ if (pLVREV_Private->pInputSave) {
+ free(pLVREV_Private->pInputSave);
+ pLVREV_Private->pInputSave = LVM_NULL;
+ }
+
+ delete pLVREV_Private;
return LVREV_SUCCESS;
}
/* End of file */
diff --git a/media/libeffects/lvm/lib/Reverb/src/LVREV_GetMemoryTable.cpp b/media/libeffects/lvm/lib/Reverb/src/LVREV_GetMemoryTable.cpp
deleted file mode 100644
index 02ceb16..0000000
--- a/media/libeffects/lvm/lib/Reverb/src/LVREV_GetMemoryTable.cpp
+++ /dev/null
@@ -1,184 +0,0 @@
-/*
- * Copyright (C) 2004-2010 NXP Software
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/****************************************************************************************/
-/* */
-/* Includes */
-/* */
-/****************************************************************************************/
-#include "LVREV_Private.h"
-#include "InstAlloc.h"
-
-/****************************************************************************************/
-/* */
-/* FUNCTION: LVREV_GetMemoryTable */
-/* */
-/* DESCRIPTION: */
-/* This function is used for memory allocation and free. It can be called in */
-/* two ways: */
-/* */
-/* hInstance = NULL Returns the memory requirements */
-/* hInstance = Instance handle Returns the memory requirements and allocated */
-/* base addresses. */
-/* */
-/* When this function is called for memory allocation (hInstance=NULL) the memory */
-/* base address pointers are NULL on return. */
-/* */
-/* When the function is called for free (hInstance = Instance Handle) the memory */
-/* table returns the allocated memory and base addresses used during initialisation. */
-/* */
-/* PARAMETERS: */
-/* hInstance Instance Handle */
-/* pMemoryTable Pointer to an empty memory table */
-/* pInstanceParams Pointer to the instance parameters */
-/* */
-/* RETURNS: */
-/* LVREV_Success Succeeded */
-/* LVREV_NULLADDRESS When pMemoryTable is NULL */
-/* LVREV_NULLADDRESS When requesting memory requirements and pInstanceParams */
-/* is NULL */
-/* */
-/* NOTES: */
-/* 1. This function may be interrupted by the LVREV_Process function */
-/* */
-/****************************************************************************************/
-LVREV_ReturnStatus_en LVREV_GetMemoryTable(LVREV_Handle_t hInstance,
- LVREV_MemoryTable_st* pMemoryTable,
- LVREV_InstanceParams_st* pInstanceParams) {
- INST_ALLOC SlowData;
- INST_ALLOC FastData;
- INST_ALLOC FastCoef;
- INST_ALLOC Temporary;
- LVM_UINT16 MaxBlockSize;
-
- /*
- * Check for error conditions
- */
- /* Check for NULL pointer */
- if (pMemoryTable == LVM_NULL) {
- return (LVREV_NULLADDRESS);
- }
-
- /*
- * Check all instance parameters are in range
- */
- if (pInstanceParams != LVM_NULL) {
- /*
- * Call for memory allocation, so check the parameters
- */
- /* Check for a non-zero block size */
- if (pInstanceParams->MaxBlockSize == 0) {
- return LVREV_OUTOFRANGE;
- }
-
- /* Check for a valid number of delay lines */
- if ((pInstanceParams->NumDelays != LVREV_DELAYLINES_1) &&
- (pInstanceParams->NumDelays != LVREV_DELAYLINES_2) &&
- (pInstanceParams->NumDelays != LVREV_DELAYLINES_4)) {
- return LVREV_OUTOFRANGE;
- }
- }
-
- /*
- * Initialise the InstAlloc instances
- */
- InstAlloc_Init(&SlowData, (void*)LVM_NULL);
- InstAlloc_Init(&FastData, (void*)LVM_NULL);
- InstAlloc_Init(&FastCoef, (void*)LVM_NULL);
- InstAlloc_Init(&Temporary, (void*)LVM_NULL);
-
- /*
- * Fill in the memory table
- */
- if (hInstance == LVM_NULL) {
- /*
- * Check for null pointers
- */
- if (pInstanceParams == LVM_NULL) {
- return (LVREV_NULLADDRESS);
- }
-
- /*
- * Select the maximum internal block size
- */
- if (pInstanceParams->NumDelays == LVREV_DELAYLINES_4) {
- MaxBlockSize = LVREV_MAX_AP_DELAY[3];
- } else if (pInstanceParams->NumDelays == LVREV_DELAYLINES_2) {
- MaxBlockSize = LVREV_MAX_AP_DELAY[1];
- } else {
- MaxBlockSize = LVREV_MAX_AP_DELAY[0];
- }
-
- if (MaxBlockSize > pInstanceParams->MaxBlockSize) {
- MaxBlockSize = pInstanceParams->MaxBlockSize;
- }
-
- /*
- * Slow data memory
- */
- InstAlloc_AddMember(&SlowData, sizeof(LVREV_Instance_st));
- pMemoryTable->Region[LVM_PERSISTENT_SLOW_DATA].Size = InstAlloc_GetTotal(&SlowData);
- pMemoryTable->Region[LVM_PERSISTENT_SLOW_DATA].Type = LVM_PERSISTENT_SLOW_DATA;
- pMemoryTable->Region[LVM_PERSISTENT_SLOW_DATA].pBaseAddress = LVM_NULL;
-
- /*
- * Persistent fast data memory
- */
- for (size_t i = 0; i < pInstanceParams->NumDelays; i++) {
- InstAlloc_AddMember(&FastData, LVREV_MAX_T_DELAY[i] * sizeof(LVM_FLOAT));
- }
-
- pMemoryTable->Region[LVM_PERSISTENT_FAST_DATA].Size = InstAlloc_GetTotal(&FastData);
- pMemoryTable->Region[LVM_PERSISTENT_FAST_DATA].Type = LVM_PERSISTENT_FAST_DATA;
- pMemoryTable->Region[LVM_PERSISTENT_FAST_DATA].pBaseAddress = LVM_NULL;
-
- /*
- * Persistent fast coefficient memory
- */
- pMemoryTable->Region[LVM_PERSISTENT_FAST_COEF].Size = InstAlloc_GetTotal(&FastCoef);
- pMemoryTable->Region[LVM_PERSISTENT_FAST_COEF].Type = LVM_PERSISTENT_FAST_COEF;
- pMemoryTable->Region[LVM_PERSISTENT_FAST_COEF].pBaseAddress = LVM_NULL;
-
- /*
- * Temporary fast memory
- */
- /* General purpose scratch memory */
- InstAlloc_AddMember(&Temporary, sizeof(LVM_FLOAT) * MaxBlockSize);
- /* Mono->stereo input saved for end mix */
- InstAlloc_AddMember(&Temporary, 2 * sizeof(LVM_FLOAT) * MaxBlockSize);
- for (size_t i = 0; i < pInstanceParams->NumDelays; i++) {
- /* A Scratch buffer for each delay line */
- InstAlloc_AddMember(&Temporary, sizeof(LVM_FLOAT) * MaxBlockSize);
- }
-
- pMemoryTable->Region[LVM_TEMPORARY_FAST].Size = InstAlloc_GetTotal(&Temporary);
- pMemoryTable->Region[LVM_TEMPORARY_FAST].Type = LVM_TEMPORARY_FAST;
- pMemoryTable->Region[LVM_TEMPORARY_FAST].pBaseAddress = LVM_NULL;
-
- } else {
- LVREV_Instance_st* pLVREV_Private = (LVREV_Instance_st*)hInstance;
-
- /*
- * Read back memory allocation table
- */
- *pMemoryTable = pLVREV_Private->MemoryTable;
- }
-
- return (LVREV_SUCCESS);
-}
-
-/* End of file */
diff --git a/media/libeffects/lvm/lib/Reverb/src/LVREV_Private.h b/media/libeffects/lvm/lib/Reverb/src/LVREV_Private.h
index 33f8165..9a2f9ca 100644
--- a/media/libeffects/lvm/lib/Reverb/src/LVREV_Private.h
+++ b/media/libeffects/lvm/lib/Reverb/src/LVREV_Private.h
@@ -103,11 +103,9 @@
/* */
/****************************************************************************************/
-
typedef struct {
/* General */
LVREV_InstanceParams_st InstanceParams; /* Initialisation time instance parameters */
- LVREV_MemoryTable_st MemoryTable; /* Memory table */
LVREV_ControlParams_st CurrentParams; /* Parameters being used */
LVREV_ControlParams_st NewParams; /* New parameters from the \
calling application */
diff --git a/media/libeffects/lvm/lib/SpectrumAnalyzer/src/LVPSA_Init.cpp b/media/libeffects/lvm/lib/SpectrumAnalyzer/src/LVPSA_Init.cpp
index 5550b9c..a0f28b1 100644
--- a/media/libeffects/lvm/lib/SpectrumAnalyzer/src/LVPSA_Init.cpp
+++ b/media/libeffects/lvm/lib/SpectrumAnalyzer/src/LVPSA_Init.cpp
@@ -18,7 +18,6 @@
#include <stdlib.h>
#include "LVPSA.h"
#include "LVPSA_Private.h"
-#include "InstAlloc.h"
/************************************************************************************/
/* */
diff --git a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Equaliser.cpp b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Equaliser.cpp
index 1746786..2b628f1 100644
--- a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Equaliser.cpp
+++ b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Equaliser.cpp
@@ -72,8 +72,8 @@
std::array<LVM_FLOAT, android::audio_utils::kBiquadNumCoefs> coefs = {
pEqualiserCoefTable[Offset].A0, pEqualiserCoefTable[Offset].A1,
- pEqualiserCoefTable[Offset].A2, -(pEqualiserCoefTable[Offset].B1),
- -(pEqualiserCoefTable[Offset].B2)};
+ pEqualiserCoefTable[Offset].A2, pEqualiserCoefTable[Offset].B1,
+ pEqualiserCoefTable[Offset].B2};
pInstance->pEqBiquad.reset(new android::audio_utils::BiquadFilter<LVM_FLOAT>(
(pParams->NrChannels == FCC_1) ? FCC_1 : FCC_2, coefs));
}
diff --git a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_ReverbGenerator.cpp b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_ReverbGenerator.cpp
index 12b1dc3..c5b6598 100644
--- a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_ReverbGenerator.cpp
+++ b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_ReverbGenerator.cpp
@@ -93,8 +93,8 @@
std::array<LVM_FLOAT, android::audio_utils::kBiquadNumCoefs> coefs = {
pReverbCoefTable[Offset].A0, pReverbCoefTable[Offset].A1,
- pReverbCoefTable[Offset].A2, -(pReverbCoefTable[Offset].B1),
- -(pReverbCoefTable[Offset].B2)};
+ pReverbCoefTable[Offset].A2, pReverbCoefTable[Offset].B1,
+ pReverbCoefTable[Offset].B2};
pInstance->pRevBiquad.reset(new android::audio_utils::BiquadFilter<LVM_FLOAT>(
(pParams->NrChannels == FCC_1) ? FCC_1 : FCC_2, coefs));
diff --git a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_StereoEnhancer.cpp b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_StereoEnhancer.cpp
index e3ff604..3ca25f9 100644
--- a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_StereoEnhancer.cpp
+++ b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_StereoEnhancer.cpp
@@ -68,7 +68,7 @@
std::array<LVM_FLOAT, android::audio_utils::kBiquadNumCoefs> coefs = {
LVCS_SEMidCoefTable[Offset].A0, LVCS_SEMidCoefTable[Offset].A1, 0.0,
- -(LVCS_SEMidCoefTable[Offset].B1), 0.0};
+ LVCS_SEMidCoefTable[Offset].B1, 0.0};
pInstance->pSEMidBiquad.reset(
new android::audio_utils::BiquadFilter<LVM_FLOAT>(FCC_1, coefs));
@@ -77,7 +77,7 @@
/* Side filter */
coefs = {pSESideCoefs[Offset].A0, pSESideCoefs[Offset].A1, pSESideCoefs[Offset].A2,
- -(pSESideCoefs[Offset].B1), -(pSESideCoefs[Offset].B2)};
+ pSESideCoefs[Offset].B1, pSESideCoefs[Offset].B2};
pInstance->pSESideBiquad.reset(
new android::audio_utils::BiquadFilter<LVM_FLOAT>(FCC_1, coefs));
}
diff --git a/media/libeffects/lvm/tests/reverb_test.cpp b/media/libeffects/lvm/tests/reverb_test.cpp
index 0ea401c..dfb6970 100644
--- a/media/libeffects/lvm/tests/reverb_test.cpp
+++ b/media/libeffects/lvm/tests/reverb_test.cpp
@@ -212,7 +212,9 @@
printUsage();
return EXIT_FAILURE;
}
-
+ for (int i = 1; i < argc; i++) {
+ printf("%s ", argv[i]);
+ }
reverbConfigParams_t revConfigParams{}; // default initialize
const char* inputFile = nullptr;
const char* outputFile = nullptr;
diff --git a/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp b/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
index 3738d62..290a7b1 100644
--- a/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
+++ b/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
@@ -516,25 +516,6 @@
void Reverb_free(ReverbContext* pContext) {
LVREV_ReturnStatus_en LvmStatus = LVREV_SUCCESS; /* Function call status */
- LVREV_MemoryTable_st MemTab;
-
- /* Free the algorithm memory */
- LvmStatus = LVREV_GetMemoryTable(pContext->hInstance, &MemTab, LVM_NULL);
-
- LVM_ERROR_CHECK(LvmStatus, "LVM_GetMemoryTable", "Reverb_free")
-
- for (int i = 0; i < LVM_NR_MEMORY_REGIONS; i++) {
- if (MemTab.Region[i].Size != 0) {
- if (MemTab.Region[i].pBaseAddress != NULL) {
- free(MemTab.Region[i].pBaseAddress);
- } else {
- ALOGV("\tLVM_ERROR : free() - trying to free with NULL pointer %" PRIu32
- " bytes "
- "for region %u at %p ERROR\n",
- MemTab.Region[i].Size, i, MemTab.Region[i].pBaseAddress);
- }
- }
- }
LvmStatus = LVREV_FreeInstance(pContext->hInstance);
LVM_ERROR_CHECK(LvmStatus, "LVREV_FreeInstance", "Reverb_free")
@@ -677,65 +658,17 @@
LVREV_ReturnStatus_en LvmStatus = LVREV_SUCCESS; /* Function call status */
LVREV_ControlParams_st params; /* Control Parameters */
LVREV_InstanceParams_st InstParams; /* Instance parameters */
- LVREV_MemoryTable_st MemTab; /* Memory allocation table */
- bool bMallocFailure = LVM_FALSE;
/* Set the capabilities */
InstParams.MaxBlockSize = MAX_CALL_SIZE;
InstParams.SourceFormat = LVM_STEREO; // Max format, could be mono during process
InstParams.NumDelays = LVREV_DELAYLINES_4;
- /* Allocate memory, forcing alignment */
- LvmStatus = LVREV_GetMemoryTable(LVM_NULL, &MemTab, &InstParams);
-
- LVM_ERROR_CHECK(LvmStatus, "LVREV_GetMemoryTable", "Reverb_init")
- if (LvmStatus != LVREV_SUCCESS) return -EINVAL;
-
- ALOGV("\tCreateInstance Successfully called LVM_GetMemoryTable\n");
-
- /* Allocate memory */
- for (int i = 0; i < LVM_NR_MEMORY_REGIONS; i++) {
- if (MemTab.Region[i].Size != 0) {
- MemTab.Region[i].pBaseAddress = calloc(1, MemTab.Region[i].Size);
-
- if (MemTab.Region[i].pBaseAddress == LVM_NULL) {
- ALOGV("\tLVREV_ERROR :Reverb_init CreateInstance Failed to allocate %" PRIu32
- " bytes for region %u\n",
- MemTab.Region[i].Size, i);
- bMallocFailure = LVM_TRUE;
- } else {
- ALOGV("\tReverb_init CreateInstance allocate %" PRIu32
- " bytes for region %u at %p\n",
- MemTab.Region[i].Size, i, MemTab.Region[i].pBaseAddress);
- }
- }
- }
-
- /* If one or more of the memory regions failed to allocate, free the regions that were
- * succesfully allocated and return with an error
- */
- if (bMallocFailure == LVM_TRUE) {
- for (int i = 0; i < LVM_NR_MEMORY_REGIONS; i++) {
- if (MemTab.Region[i].pBaseAddress == LVM_NULL) {
- ALOGV("\tLVM_ERROR :Reverb_init CreateInstance Failed to allocate %" PRIu32
- " bytes for region %u - Not freeing\n",
- MemTab.Region[i].Size, i);
- } else {
- ALOGV("\tLVM_ERROR :Reverb_init CreateInstance Failed: but allocated %" PRIu32
- " bytes for region %u at %p- free\n",
- MemTab.Region[i].Size, i, MemTab.Region[i].pBaseAddress);
- free(MemTab.Region[i].pBaseAddress);
- }
- }
- return -EINVAL;
- }
- ALOGV("\tReverb_init CreateInstance Successfully malloc'd memory\n");
-
/* Initialise */
pContext->hInstance = LVM_NULL;
/* Init sets the instance handle */
- LvmStatus = LVREV_GetInstanceHandle(&pContext->hInstance, &MemTab, &InstParams);
+ LvmStatus = LVREV_GetInstanceHandle(&pContext->hInstance, &InstParams);
LVM_ERROR_CHECK(LvmStatus, "LVM_GetInstanceHandle", "Reverb_init")
if (LvmStatus != LVREV_SUCCESS) return -EINVAL;
diff --git a/media/libeffects/preprocessing/Android.bp b/media/libeffects/preprocessing/Android.bp
index eb3ce34..87ed8b6 100644
--- a/media/libeffects/preprocessing/Android.bp
+++ b/media/libeffects/preprocessing/Android.bp
@@ -22,6 +22,7 @@
name: "libaudiopreprocessing",
vendor: true,
relative_install_path: "soundfx",
+ host_supported: true,
srcs: ["PreProcessing.cpp"],
local_include_dirs: [
".",
@@ -47,4 +48,9 @@
"libhardware_headers",
"libwebrtc_absl_headers",
],
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
}
diff --git a/media/libeffects/preprocessing/PreProcessing.cpp b/media/libeffects/preprocessing/PreProcessing.cpp
index 03ccc34..3b0b6d6 100644
--- a/media/libeffects/preprocessing/PreProcessing.cpp
+++ b/media/libeffects/preprocessing/PreProcessing.cpp
@@ -670,8 +670,8 @@
return 0;
}
-int NsGetParameter(preproc_effect_t* effect __unused, void* pParam __unused,
- uint32_t* pValueSize __unused, void* pValue __unused) {
+int NsGetParameter(preproc_effect_t* /*effect __unused*/, void* /*pParam __unused*/,
+ uint32_t* /*pValueSize __unused*/, void* /*pValue __unused*/) {
int status = 0;
return status;
}
@@ -910,7 +910,7 @@
session->apm = NULL;
delete session->inBuf;
session->inBuf = NULL;
- delete session->outBuf;
+ free(session->outBuf);
session->outBuf = NULL;
delete session->revBuf;
session->revBuf = NULL;
@@ -1551,7 +1551,7 @@
}
int PreProcessingFx_ProcessReverse(effect_handle_t self, audio_buffer_t* inBuffer,
- audio_buffer_t* outBuffer __unused) {
+ audio_buffer_t* outBuffer) {
preproc_effect_t* effect = (preproc_effect_t*)self;
if (effect == NULL) {
diff --git a/media/libeffects/preprocessing/tests/Android.bp b/media/libeffects/preprocessing/tests/Android.bp
index 8848e79..18c6c98 100644
--- a/media/libeffects/preprocessing/tests/Android.bp
+++ b/media/libeffects/preprocessing/tests/Android.bp
@@ -11,8 +11,39 @@
}
cc_test {
+ name: "EffectPreprocessingTest",
+ vendor: true,
+ gtest: true,
+ host_supported: true,
+ test_suites: ["device-tests"],
+ srcs: [
+ "EffectPreprocessingTest.cpp",
+ "EffectTestHelper.cpp",
+ ],
+ static_libs: [
+ "libaudiopreprocessing",
+ "libaudioutils",
+ "webrtc_audio_processing",
+ ],
+ shared_libs: [
+ "liblog",
+ ],
+ header_libs: [
+ "libaudioeffects",
+ "libhardware_headers",
+ ],
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
+}
+
+cc_test {
name: "AudioPreProcessingTest",
vendor: true,
+ host_supported: true,
+ gtest: false,
srcs: ["PreProcessingTest.cpp"],
shared_libs: [
"libaudioutils",
@@ -27,7 +58,11 @@
"libaudioeffects",
"libhardware_headers",
],
- gtest: false,
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
}
cc_test {
diff --git a/media/libeffects/preprocessing/tests/EffectPreprocessingTest.cpp b/media/libeffects/preprocessing/tests/EffectPreprocessingTest.cpp
new file mode 100644
index 0000000..07006a1
--- /dev/null
+++ b/media/libeffects/preprocessing/tests/EffectPreprocessingTest.cpp
@@ -0,0 +1,332 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "EffectTestHelper.h"
+
+#include <getopt.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <tuple>
+#include <vector>
+
+#include <audio_effects/effect_aec.h>
+#include <audio_effects/effect_agc.h>
+#include <audio_effects/effect_agc2.h>
+#include <audio_effects/effect_ns.h>
+#include <log/log.h>
+
+constexpr effect_uuid_t kAGCUuid = {
+ 0xaa8130e0, 0x66fc, 0x11e0, 0xbad0, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}};
+constexpr effect_uuid_t kAGC2Uuid = {
+ 0x89f38e65, 0xd4d2, 0x4d64, 0xad0e, {0x2b, 0x3e, 0x79, 0x9e, 0xa8, 0x86}};
+constexpr effect_uuid_t kAECUuid = {
+ 0xbb392ec0, 0x8d4d, 0x11e0, 0xa896, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}};
+constexpr effect_uuid_t kNSUuid = {
+ 0xc06c8400, 0x8e06, 0x11e0, 0x9cb6, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}};
+
+static bool isAGCEffect(const effect_uuid_t* uuid) {
+ return uuid == &kAGCUuid;
+}
+static bool isAGC2Effect(const effect_uuid_t* uuid) {
+ return uuid == &kAGC2Uuid;
+}
+static bool isAECEffect(const effect_uuid_t* uuid) {
+ return uuid == &kAECUuid;
+}
+static bool isNSEffect(const effect_uuid_t* uuid) {
+ return uuid == &kNSUuid;
+}
+
+constexpr int kAGCTargetLevels[] = {0, -300, -500, -1000, -3100};
+
+constexpr int kAGCCompLevels[] = {0, -300, -500, -1000, -9000};
+
+constexpr size_t kAGC2FixedDigitalGains[] = {0, 3, 10, 20, 49};
+
+constexpr size_t kAGC2AdaptGigitalLevelEstimators[] = {0, 1};
+
+constexpr size_t kAGC2ExtraSaturationMargins[] = {0, 3, 10, 20, 100};
+
+constexpr size_t kAECEchoDelays[] = {0, 250, 500};
+
+constexpr size_t kNSLevels[] = {0, 1, 2, 3};
+
+struct AGCParams {
+ int targetLevel;
+ int compLevel;
+};
+
+struct AGC2Params {
+ size_t fixedDigitalGain;
+ size_t adaptDigiLevelEstimator;
+ size_t extraSaturationMargin;
+};
+
+struct AECParams {
+ size_t echoDelay;
+};
+
+struct NSParams {
+ size_t level;
+};
+
+struct PreProcParams {
+ const effect_uuid_t* uuid;
+ union {
+ AGCParams agcParams;
+ AGC2Params agc2Params;
+ AECParams aecParams;
+ NSParams nsParams;
+ };
+};
+
+// Create a list of pre-processing parameters to be used for testing
+static const std::vector<PreProcParams> kPreProcParams = [] {
+ std::vector<PreProcParams> result;
+
+ for (const auto targetLevel : kAGCTargetLevels) {
+ for (const auto compLevel : kAGCCompLevels) {
+ AGCParams agcParams = {.targetLevel = targetLevel, .compLevel = compLevel};
+ PreProcParams params = {.uuid = &kAGCUuid, .agcParams = agcParams};
+ result.push_back(params);
+ }
+ }
+
+ for (const auto fixedDigitalGain : kAGC2FixedDigitalGains) {
+ for (const auto adaptDigiLevelEstimator : kAGC2AdaptGigitalLevelEstimators) {
+ for (const auto extraSaturationMargin : kAGC2ExtraSaturationMargins) {
+ AGC2Params agc2Params = {.fixedDigitalGain = fixedDigitalGain,
+ .adaptDigiLevelEstimator = adaptDigiLevelEstimator,
+ .extraSaturationMargin = extraSaturationMargin};
+ PreProcParams params = {.uuid = &kAGC2Uuid, .agc2Params = agc2Params};
+ result.push_back(params);
+ }
+ }
+ }
+
+ for (const auto echoDelay : kAECEchoDelays) {
+ AECParams aecParams = {.echoDelay = echoDelay};
+ PreProcParams params = {.uuid = &kAECUuid, .aecParams = aecParams};
+ result.push_back(params);
+ }
+
+ for (const auto level : kNSLevels) {
+ NSParams nsParams = {.level = level};
+ PreProcParams params = {.uuid = &kNSUuid, .nsParams = nsParams};
+ result.push_back(params);
+ }
+ return result;
+}();
+
+static const size_t kNumPreProcParams = std::size(kPreProcParams);
+
+void setPreProcParams(const effect_uuid_t* uuid, EffectTestHelper& effect, size_t paramIdx) {
+ const PreProcParams* params = &kPreProcParams[paramIdx];
+ if (isAGCEffect(uuid)) {
+ const AGCParams* agcParams = ¶ms->agcParams;
+ ASSERT_NO_FATAL_FAILURE(effect.setParam(AGC_PARAM_TARGET_LEVEL, agcParams->targetLevel));
+ ASSERT_NO_FATAL_FAILURE(effect.setParam(AGC_PARAM_COMP_GAIN, agcParams->compLevel));
+ } else if (isAGC2Effect(uuid)) {
+ const AGC2Params* agc2Params = ¶ms->agc2Params;
+ ASSERT_NO_FATAL_FAILURE(
+ effect.setParam(AGC2_PARAM_FIXED_DIGITAL_GAIN, agc2Params->fixedDigitalGain));
+ ASSERT_NO_FATAL_FAILURE(effect.setParam(AGC2_PARAM_ADAPT_DIGI_LEVEL_ESTIMATOR,
+ agc2Params->adaptDigiLevelEstimator));
+ ASSERT_NO_FATAL_FAILURE(effect.setParam(AGC2_PARAM_ADAPT_DIGI_EXTRA_SATURATION_MARGIN,
+ agc2Params->extraSaturationMargin));
+ } else if (isAECEffect(uuid)) {
+ const AECParams* aecParams = ¶ms->aecParams;
+ ASSERT_NO_FATAL_FAILURE(effect.setParam(AEC_PARAM_ECHO_DELAY, aecParams->echoDelay));
+ } else if (isNSEffect(uuid)) {
+ const NSParams* nsParams = ¶ms->nsParams;
+ ASSERT_NO_FATAL_FAILURE(effect.setParam(NS_PARAM_LEVEL, nsParams->level));
+ }
+}
+
+typedef std::tuple<int, int, int, int> SingleEffectTestParam;
+class SingleEffectTest : public ::testing::TestWithParam<SingleEffectTestParam> {
+ public:
+ SingleEffectTest()
+ : mSampleRate(EffectTestHelper::kSampleRates[std::get<1>(GetParam())]),
+ mFrameCount(mSampleRate * EffectTestHelper::kTenMilliSecVal),
+ mLoopCount(EffectTestHelper::kLoopCounts[std::get<2>(GetParam())]),
+ mTotalFrameCount(mFrameCount * mLoopCount),
+ mChMask(EffectTestHelper::kChMasks[std::get<0>(GetParam())]),
+ mChannelCount(audio_channel_count_from_in_mask(mChMask)),
+ mParamIdx(std::get<3>(GetParam())),
+ mUuid(kPreProcParams[mParamIdx].uuid){};
+
+ const size_t mSampleRate;
+ const size_t mFrameCount;
+ const size_t mLoopCount;
+ const size_t mTotalFrameCount;
+ const size_t mChMask;
+ const size_t mChannelCount;
+ const size_t mParamIdx;
+ const effect_uuid_t* mUuid;
+};
+
+// Tests applying a single effect
+TEST_P(SingleEffectTest, SimpleProcess) {
+ SCOPED_TRACE(testing::Message() << " chMask: " << mChMask << " sampleRate: " << mSampleRate
+ << " loopCount: " << mLoopCount << " paramIdx " << mParamIdx);
+
+ EffectTestHelper effect(mUuid, mChMask, mSampleRate, mLoopCount);
+
+ ASSERT_NO_FATAL_FAILURE(effect.createEffect());
+ ASSERT_NO_FATAL_FAILURE(effect.setConfig(isAECEffect(mUuid)));
+ ASSERT_NO_FATAL_FAILURE(setPreProcParams(mUuid, effect, mParamIdx));
+
+ // Initialize input buffer with deterministic pseudo-random values
+ std::vector<int16_t> input(mTotalFrameCount * mChannelCount);
+ std::vector<int16_t> output(mTotalFrameCount * mChannelCount);
+ std::vector<int16_t> farInput(mTotalFrameCount * mChannelCount);
+ std::minstd_rand gen(mChMask);
+ std::uniform_int_distribution<int16_t> dis(INT16_MIN, INT16_MAX);
+ for (auto& in : input) {
+ in = dis(gen);
+ }
+ if (isAECEffect(mUuid)) {
+ for (auto& farIn : farInput) {
+ farIn = dis(gen);
+ }
+ }
+ ASSERT_NO_FATAL_FAILURE(effect.process(input.data(), output.data(), isAECEffect(mUuid)));
+ if (isAECEffect(mUuid)) {
+ ASSERT_NO_FATAL_FAILURE(effect.process_reverse(farInput.data(), output.data()));
+ }
+ ASSERT_NO_FATAL_FAILURE(effect.releaseEffect());
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ PreProcTestAll, SingleEffectTest,
+ ::testing::Combine(::testing::Range(0, (int)EffectTestHelper::kNumChMasks),
+ ::testing::Range(0, (int)EffectTestHelper::kNumSampleRates),
+ ::testing::Range(0, (int)EffectTestHelper::kNumLoopCounts),
+ ::testing::Range(0, (int)kNumPreProcParams)));
+
+typedef std::tuple<int, int, int> SingleEffectComparisonTestParam;
+class SingleEffectComparisonTest
+ : public ::testing::TestWithParam<SingleEffectComparisonTestParam> {
+ public:
+ SingleEffectComparisonTest()
+ : mSampleRate(EffectTestHelper::kSampleRates[std::get<0>(GetParam())]),
+ mFrameCount(mSampleRate * EffectTestHelper::kTenMilliSecVal),
+ mLoopCount(EffectTestHelper::kLoopCounts[std::get<1>(GetParam())]),
+ mTotalFrameCount(mFrameCount * mLoopCount),
+ mParamIdx(std::get<2>(GetParam())),
+ mUuid(kPreProcParams[mParamIdx].uuid){};
+
+ const size_t mSampleRate;
+ const size_t mFrameCount;
+ const size_t mLoopCount;
+ const size_t mTotalFrameCount;
+ const size_t mParamIdx;
+ const effect_uuid_t* mUuid;
+};
+
+// Compares first channel in multi-channel output to mono output when same effect is applied
+TEST_P(SingleEffectComparisonTest, SimpleProcess) {
+ SCOPED_TRACE(testing::Message() << " sampleRate: " << mSampleRate
+ << " loopCount: " << mLoopCount << " paramIdx " << mParamIdx);
+
+ // Initialize mono input buffer with deterministic pseudo-random values
+ std::vector<int16_t> monoInput(mTotalFrameCount);
+ std::vector<int16_t> monoFarInput(mTotalFrameCount);
+
+ std::minstd_rand gen(mSampleRate);
+ std::uniform_int_distribution<int16_t> dis(INT16_MIN, INT16_MAX);
+ for (auto& in : monoInput) {
+ in = dis(gen);
+ }
+ if (isAECEffect(mUuid)) {
+ for (auto& farIn : monoFarInput) {
+ farIn = dis(gen);
+ }
+ }
+
+ // Apply effect on mono channel
+ EffectTestHelper monoEffect(mUuid, AUDIO_CHANNEL_INDEX_MASK_1, mSampleRate, mLoopCount);
+
+ ASSERT_NO_FATAL_FAILURE(monoEffect.createEffect());
+ ASSERT_NO_FATAL_FAILURE(monoEffect.setConfig(isAECEffect(mUuid)));
+ ASSERT_NO_FATAL_FAILURE(setPreProcParams(mUuid, monoEffect, mParamIdx));
+
+ std::vector<int16_t> monoOutput(mTotalFrameCount);
+ ASSERT_NO_FATAL_FAILURE(
+ monoEffect.process(monoInput.data(), monoOutput.data(), isAECEffect(mUuid)));
+ if (isAECEffect(mUuid)) {
+ ASSERT_NO_FATAL_FAILURE(monoEffect.process_reverse(monoFarInput.data(), monoOutput.data()));
+ }
+ ASSERT_NO_FATAL_FAILURE(monoEffect.releaseEffect());
+
+ for (size_t chMask : EffectTestHelper::kChMasks) {
+ size_t channelCount = audio_channel_count_from_in_mask(chMask);
+
+ EffectTestHelper testEffect(mUuid, chMask, mSampleRate, mLoopCount);
+
+ ASSERT_NO_FATAL_FAILURE(testEffect.createEffect());
+ ASSERT_NO_FATAL_FAILURE(testEffect.setConfig(isAECEffect(mUuid)));
+ ASSERT_NO_FATAL_FAILURE(setPreProcParams(mUuid, testEffect, mParamIdx));
+
+ std::vector<int16_t> testInput(mTotalFrameCount * channelCount);
+ std::vector<int16_t> testFarInput(mTotalFrameCount * channelCount);
+
+ // Repeat mono channel data to all the channels
+ // adjust_channels() zero fills channels > 2, hence can't be used here
+ for (size_t i = 0; i < mTotalFrameCount; ++i) {
+ auto* fpInput = &testInput[i * channelCount];
+ std::fill(fpInput, fpInput + channelCount, monoInput[i]);
+ }
+ if (isAECEffect(mUuid)) {
+ for (size_t i = 0; i < mTotalFrameCount; ++i) {
+ auto* fpFarInput = &testFarInput[i * channelCount];
+ std::fill(fpFarInput, fpFarInput + channelCount, monoFarInput[i]);
+ }
+ }
+
+ std::vector<int16_t> testOutput(mTotalFrameCount * channelCount);
+ ASSERT_NO_FATAL_FAILURE(
+ testEffect.process(testInput.data(), testOutput.data(), isAECEffect(mUuid)));
+ if (isAECEffect(mUuid)) {
+ ASSERT_NO_FATAL_FAILURE(
+ testEffect.process_reverse(testFarInput.data(), testOutput.data()));
+ }
+ ASSERT_NO_FATAL_FAILURE(testEffect.releaseEffect());
+
+ // Adjust the test output to mono channel
+ std::vector<int16_t> monoTestOutput(mTotalFrameCount);
+ adjust_channels(testOutput.data(), channelCount, monoTestOutput.data(), FCC_1,
+ sizeof(int16_t), mTotalFrameCount * sizeof(int16_t) * channelCount);
+
+ ASSERT_EQ(0, memcmp(monoOutput.data(), monoTestOutput.data(),
+ mTotalFrameCount * sizeof(int16_t)))
+ << "Mono channel output does not match with reference output \n";
+ }
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ PreProcTestAll, SingleEffectComparisonTest,
+ ::testing::Combine(::testing::Range(0, (int)EffectTestHelper::kNumSampleRates),
+ ::testing::Range(0, (int)EffectTestHelper::kNumLoopCounts),
+ ::testing::Range(0, (int)kNumPreProcParams)));
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ int status = RUN_ALL_TESTS();
+ ALOGV("Test result = %d", status);
+ return status;
+}
diff --git a/media/libeffects/preprocessing/tests/EffectTestHelper.cpp b/media/libeffects/preprocessing/tests/EffectTestHelper.cpp
new file mode 100644
index 0000000..79200b6
--- /dev/null
+++ b/media/libeffects/preprocessing/tests/EffectTestHelper.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "EffectTestHelper.h"
+extern audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM;
+
+void EffectTestHelper::createEffect() {
+ int status = AUDIO_EFFECT_LIBRARY_INFO_SYM.create_effect(mUuid, 1, 1, &mEffectHandle);
+ ASSERT_EQ(status, 0) << "create_effect returned an error " << status;
+}
+
+void EffectTestHelper::releaseEffect() {
+ int status = AUDIO_EFFECT_LIBRARY_INFO_SYM.release_effect(mEffectHandle);
+ ASSERT_EQ(status, 0) << "release_effect returned an error " << status;
+}
+
+void EffectTestHelper::setConfig(bool configReverse) {
+ effect_config_t config{};
+ config.inputCfg.samplingRate = config.outputCfg.samplingRate = mSampleRate;
+ config.inputCfg.channels = config.outputCfg.channels = mChMask;
+ config.inputCfg.format = config.outputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
+
+ int reply = 0;
+ uint32_t replySize = sizeof(reply);
+
+ int status = (*mEffectHandle)
+ ->command(mEffectHandle, EFFECT_CMD_SET_CONFIG, sizeof(effect_config_t),
+ &config, &replySize, &reply);
+ ASSERT_EQ(status, 0) << "set_config returned an error " << status;
+ ASSERT_EQ(reply, 0) << "set_config reply non zero " << reply;
+
+ if (configReverse) {
+ int status = (*mEffectHandle)
+ ->command(mEffectHandle, EFFECT_CMD_SET_CONFIG_REVERSE,
+ sizeof(effect_config_t), &config, &replySize, &reply);
+ ASSERT_EQ(status, 0) << "set_config_reverse returned an error " << status;
+ ASSERT_EQ(reply, 0) << "set_config_reverse reply non zero " << reply;
+ }
+
+ status = (*mEffectHandle)
+ ->command(mEffectHandle, EFFECT_CMD_ENABLE, 0, nullptr, &replySize, &reply);
+ ASSERT_EQ(status, 0) << "cmd_enable returned an error " << status;
+ ASSERT_EQ(reply, 0) << "cmd_enable reply non zero " << reply;
+}
+
+void EffectTestHelper::setParam(uint32_t type, uint32_t value) {
+ int reply = 0;
+ uint32_t replySize = sizeof(reply);
+ uint32_t paramData[2] = {type, value};
+ auto effectParam = (effect_param_t*)malloc(sizeof(effect_param_t) + sizeof(paramData));
+ memcpy(&effectParam->data[0], ¶mData[0], sizeof(paramData));
+ effectParam->psize = sizeof(paramData[0]);
+ effectParam->vsize = sizeof(paramData[1]);
+ int status = (*mEffectHandle)
+ ->command(mEffectHandle, EFFECT_CMD_SET_PARAM,
+ sizeof(effect_param_t) + sizeof(paramData), effectParam,
+ &replySize, &reply);
+ free(effectParam);
+ ASSERT_EQ(status, 0) << "set_param returned an error " << status;
+ ASSERT_EQ(reply, 0) << "set_param reply non zero " << reply;
+}
+
+void EffectTestHelper::process(int16_t* input, int16_t* output, bool setAecEchoDelay) {
+ audio_buffer_t inBuffer = {.frameCount = mFrameCount, .s16 = input};
+ audio_buffer_t outBuffer = {.frameCount = mFrameCount, .s16 = output};
+ for (size_t i = 0; i < mLoopCount; i++) {
+ if (setAecEchoDelay) ASSERT_NO_FATAL_FAILURE(setParam(AEC_PARAM_ECHO_DELAY, kAECDelay));
+ int status = (*mEffectHandle)->process(mEffectHandle, &inBuffer, &outBuffer);
+ ASSERT_EQ(status, 0) << "process returned an error " << status;
+
+ inBuffer.s16 += mFrameCount * mChannelCount;
+ outBuffer.s16 += mFrameCount * mChannelCount;
+ }
+}
+
+void EffectTestHelper::process_reverse(int16_t* farInput, int16_t* output) {
+ audio_buffer_t farInBuffer = {.frameCount = mFrameCount, .s16 = farInput};
+ audio_buffer_t outBuffer = {.frameCount = mFrameCount, .s16 = output};
+ for (size_t i = 0; i < mLoopCount; i++) {
+ int status = (*mEffectHandle)->process_reverse(mEffectHandle, &farInBuffer, &outBuffer);
+ ASSERT_EQ(status, 0) << "process returned an error " << status;
+
+ farInBuffer.s16 += mFrameCount * mChannelCount;
+ outBuffer.s16 += mFrameCount * mChannelCount;
+ }
+}
diff --git a/media/libeffects/preprocessing/tests/EffectTestHelper.h b/media/libeffects/preprocessing/tests/EffectTestHelper.h
new file mode 100644
index 0000000..db06823
--- /dev/null
+++ b/media/libeffects/preprocessing/tests/EffectTestHelper.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2021 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.
+ */
+
+#pragma once
+
+#include <array>
+#include <audio_effects/effect_aec.h>
+#include <audio_utils/channels.h>
+#include <audio_utils/primitives.h>
+#include <climits>
+#include <cstdlib>
+#include <gtest/gtest.h>
+#include <hardware/audio_effect.h>
+#include <log/log.h>
+#include <random>
+#include <stdint.h>
+#include <system/audio.h>
+#include <vector>
+
+template <typename T>
+static float computeSnr(const T* ref, const T* tst, size_t count) {
+ double signal{};
+ double noise{};
+
+ for (size_t i = 0; i < count; ++i) {
+ const double value(ref[i]);
+ const double diff(tst[i] - value);
+ signal += value * value;
+ noise += diff * diff;
+ }
+ // Initialized to large value to handle
+ // cases where ref and tst match exactly
+ float snr = FLT_MAX;
+ if (signal > 0.0f && noise > 0.0f) {
+ snr = 10.f * log(signal / noise);
+ }
+ return snr;
+}
+
+class EffectTestHelper {
+ public:
+ EffectTestHelper(const effect_uuid_t* uuid, size_t chMask, size_t sampleRate, size_t loopCount)
+ : mUuid(uuid),
+ mChMask(chMask),
+ mChannelCount(audio_channel_count_from_in_mask(mChMask)),
+ mSampleRate(sampleRate),
+ mFrameCount(mSampleRate * kTenMilliSecVal),
+ mLoopCount(loopCount) {}
+ void createEffect();
+ void releaseEffect();
+ void setConfig(bool configReverse);
+ void setParam(uint32_t type, uint32_t val);
+ void process(int16_t* input, int16_t* output, bool setAecEchoDelay);
+ void process_reverse(int16_t* farInput, int16_t* output);
+
+ // Corresponds to SNR for 1 bit difference between two int16_t signals
+ static constexpr float kSNRThreshold = 90.308998;
+
+ static constexpr audio_channel_mask_t kChMasks[] = {
+ AUDIO_CHANNEL_IN_MONO,
+ AUDIO_CHANNEL_IN_STEREO,
+ AUDIO_CHANNEL_IN_FRONT_BACK,
+ AUDIO_CHANNEL_IN_6,
+ AUDIO_CHANNEL_IN_2POINT0POINT2,
+ AUDIO_CHANNEL_IN_2POINT1POINT2,
+ AUDIO_CHANNEL_IN_3POINT0POINT2,
+ AUDIO_CHANNEL_IN_3POINT1POINT2,
+ AUDIO_CHANNEL_IN_5POINT1,
+ AUDIO_CHANNEL_IN_VOICE_UPLINK_MONO,
+ AUDIO_CHANNEL_IN_VOICE_DNLINK_MONO,
+ AUDIO_CHANNEL_IN_VOICE_CALL_MONO,
+ };
+
+ static constexpr float kTenMilliSecVal = 0.01;
+
+ static constexpr size_t kNumChMasks = std::size(kChMasks);
+
+ static constexpr size_t kSampleRates[] = {8000, 16000, 24000, 32000, 48000};
+
+ static constexpr size_t kNumSampleRates = std::size(kSampleRates);
+
+ static constexpr size_t kLoopCounts[] = {1, 4};
+
+ static constexpr size_t kNumLoopCounts = std::size(kLoopCounts);
+
+ static constexpr size_t kAECDelay = 0;
+
+ private:
+ const effect_uuid_t* mUuid;
+ const size_t mChMask;
+ const size_t mChannelCount;
+ const size_t mSampleRate;
+ const size_t mFrameCount;
+ const size_t mLoopCount;
+ effect_handle_t mEffectHandle{};
+};
diff --git a/media/libeffects/preprocessing/tests/PreProcessingTest.cpp b/media/libeffects/preprocessing/tests/PreProcessingTest.cpp
index 5f223c9..e0025fe 100644
--- a/media/libeffects/preprocessing/tests/PreProcessingTest.cpp
+++ b/media/libeffects/preprocessing/tests/PreProcessingTest.cpp
@@ -24,6 +24,7 @@
#include <audio_effects/effect_agc.h>
#include <audio_effects/effect_agc2.h>
#include <audio_effects/effect_ns.h>
+#include <audio_utils/channels.h>
#include <log/log.h>
// This is the only symbol that needs to be imported
@@ -55,7 +56,9 @@
ARG_NS_LVL,
ARG_AGC2_GAIN,
ARG_AGC2_LVL,
- ARG_AGC2_SAT_MGN
+ ARG_AGC2_SAT_MGN,
+ ARG_FILE_CHANNELS,
+ ARG_MONO_MODE
};
struct preProcConfigParams_t {
@@ -68,6 +71,8 @@
float agc2SaturationMargin = 2.f; // in dB
int agc2Level = 0; // either kRms(0) or kPeak(1)
int aecDelay = 0; // in ms
+ int fileChannels = 1;
+ int monoMode = 0;
};
const effect_uuid_t kPreProcUuids[PREPROC_NUM_EFFECTS] = {
@@ -106,7 +111,7 @@
printf("\n Prints this usage information");
printf("\n --fs <sampling_freq>");
printf("\n Sampling frequency in Hz, default 16000.");
- printf("\n -ch_mask <channel_mask>\n");
+ printf("\n --ch_mask <channel_mask>\n");
printf("\n 0 - AUDIO_CHANNEL_IN_MONO");
printf("\n 1 - AUDIO_CHANNEL_IN_STEREO");
printf("\n 2 - AUDIO_CHANNEL_IN_FRONT_BACK");
@@ -144,6 +149,10 @@
printf("\n AGC Adaptive Digital Saturation Margin in dB, default value 2dB");
printf("\n --aec_delay <delay>");
printf("\n AEC delay value in ms, default value 0ms");
+ printf("\n --fch <fileChannels>");
+ printf("\n number of channels in the input file");
+ printf("\n --mono <Mono Mode>");
+ printf("\n Mode to make data of all channels the same as first channel");
printf("\n");
}
@@ -189,10 +198,17 @@
printUsage();
return EXIT_FAILURE;
}
+
+ // Print the arguments passed
+ for (int i = 1; i < argc; i++) {
+ printf("%s ", argv[i]);
+ }
+
const char* inputFile = nullptr;
const char* outputFile = nullptr;
const char* farFile = nullptr;
int effectEn[PREPROC_NUM_EFFECTS] = {0};
+ struct preProcConfigParams_t preProcCfgParams {};
const option long_opts[] = {
{"help", no_argument, nullptr, ARG_HELP},
@@ -212,9 +228,10 @@
{"agc", no_argument, &effectEn[PREPROC_AGC], 1},
{"agc2", no_argument, &effectEn[PREPROC_AGC2], 1},
{"ns", no_argument, &effectEn[PREPROC_NS], 1},
+ {"fch", required_argument, nullptr, ARG_FILE_CHANNELS},
+ {"mono", no_argument, &preProcCfgParams.monoMode, 1},
{nullptr, 0, nullptr, 0},
};
- struct preProcConfigParams_t preProcCfgParams {};
while (true) {
const int opt = getopt_long(argc, (char* const*)argv, "i:o:", long_opts, nullptr);
@@ -279,6 +296,14 @@
preProcCfgParams.nsLevel = atoi(optarg);
break;
}
+ case ARG_FILE_CHANNELS: {
+ preProcCfgParams.fileChannels = atoi(optarg);
+ break;
+ }
+ case ARG_MONO_MODE: {
+ preProcCfgParams.monoMode = 1;
+ break;
+ }
default:
break;
}
@@ -402,16 +427,28 @@
// Process Call
const int frameLength = (int)(preProcCfgParams.samplingFreq * kTenMilliSecVal);
const int ioChannelCount = audio_channel_count_from_in_mask(preProcCfgParams.chMask);
+ const int fileChannelCount = preProcCfgParams.fileChannels;
const int ioFrameSize = ioChannelCount * sizeof(short);
+ const int inFrameSize = fileChannelCount * sizeof(short);
int frameCounter = 0;
while (true) {
std::vector<short> in(frameLength * ioChannelCount);
std::vector<short> out(frameLength * ioChannelCount);
std::vector<short> farIn(frameLength * ioChannelCount);
- size_t samplesRead = fread(in.data(), ioFrameSize, frameLength, inputFp.get());
+ size_t samplesRead = fread(in.data(), inFrameSize, frameLength, inputFp.get());
if (samplesRead == 0) {
break;
}
+ if (fileChannelCount != ioChannelCount) {
+ adjust_channels(in.data(), fileChannelCount, in.data(), ioChannelCount, sizeof(short),
+ frameLength * inFrameSize);
+ if (preProcCfgParams.monoMode == 1) {
+ for (int i = 0; i < frameLength; ++i) {
+ auto* fp = &in[i * ioChannelCount];
+ std::fill(fp + 1, fp + ioChannelCount, *fp); // replicate ch 0
+ }
+ }
+ }
audio_buffer_t inputBuffer, outputBuffer;
audio_buffer_t farInBuffer{};
inputBuffer.frameCount = samplesRead;
@@ -420,10 +457,21 @@
outputBuffer.s16 = out.data();
if (farFp != nullptr) {
- samplesRead = fread(farIn.data(), ioFrameSize, frameLength, farFp.get());
+ samplesRead = fread(farIn.data(), inFrameSize, frameLength, farFp.get());
if (samplesRead == 0) {
break;
}
+ if (fileChannelCount != ioChannelCount) {
+ adjust_channels(farIn.data(), fileChannelCount, farIn.data(), ioChannelCount,
+ sizeof(short), frameLength * inFrameSize);
+ if (preProcCfgParams.monoMode == 1) {
+ for (int i = 0; i < frameLength; ++i) {
+ auto* fp = &farIn[i * ioChannelCount];
+ std::fill(fp + 1, fp + ioChannelCount, *fp); // replicate ch 0
+ }
+ }
+ }
+
farInBuffer.frameCount = samplesRead;
farInBuffer.s16 = farIn.data();
}
@@ -458,8 +506,12 @@
}
}
if (outputFp != nullptr) {
+ if (fileChannelCount != ioChannelCount) {
+ adjust_channels(out.data(), ioChannelCount, out.data(), fileChannelCount,
+ sizeof(short), frameLength * ioFrameSize);
+ }
size_t samplesWritten =
- fwrite(out.data(), ioFrameSize, outputBuffer.frameCount, outputFp.get());
+ fwrite(out.data(), inFrameSize, outputBuffer.frameCount, outputFp.get());
if (samplesWritten != outputBuffer.frameCount) {
ALOGE("\nError: Output file writing failed");
break;
diff --git a/media/libeffects/preprocessing/tests/build_and_run_all_unit_tests.sh b/media/libeffects/preprocessing/tests/build_and_run_all_unit_tests.sh
new file mode 100755
index 0000000..942f2ec
--- /dev/null
+++ b/media/libeffects/preprocessing/tests/build_and_run_all_unit_tests.sh
@@ -0,0 +1,115 @@
+#!/bin/bash
+#
+# Run tests in this directory.
+#
+
+if [ -z "$ANDROID_BUILD_TOP" ]; then
+ echo "Android build environment not set"
+ exit -1
+fi
+
+# ensure we have mm
+. $ANDROID_BUILD_TOP/build/envsetup.sh
+
+mm -j
+
+echo "waiting for device"
+
+adb root && adb wait-for-device remount
+
+# location of test files
+testdir="/data/local/tmp/AudioPreProcessingTest"
+
+echo "========================================"
+echo "testing PreProcessing modules"
+adb shell mkdir -p $testdir
+adb push $ANDROID_BUILD_TOP/frameworks/av/media/libeffects/res/raw/sinesweepraw.raw $testdir
+adb push $OUT/testcases/snr/arm64/snr $testdir
+
+E_VAL=1
+if [ -z "$1" ]
+then
+ cmds=("adb push $OUT/testcases/AudioPreProcessingTest/arm64/AudioPreProcessingTest $testdir"
+ "adb push $OUT/testcases/AudioPreProcessingTest/arm/AudioPreProcessingTest $testdir"
+)
+elif [ "$1" == "32" ]
+then
+ cmds="adb push $OUT/testcases/AudioPreProcessingTest/arm/AudioPreProcessingTest $testdir"
+elif [ "$1" == "64" ]
+then
+ cmds="adb push $OUT/testcases/AudioPreProcessingTest/arm64/AudioPreProcessingTest $testdir"
+else
+ echo ""
+ echo "Invalid \"val\""
+ echo "Usage:"
+ echo " "$0" [val]"
+ echo " where, val can be either 32 or 64."
+ echo ""
+ echo " If val is not specified then both 32 bit and 64 bit binaries"
+ echo " are tested."
+ exit $E_VAL
+fi
+
+flags_arr=(
+ "--agc --mono"
+ "--ns --mono"
+ "--agc2 --mono"
+ "--aec --mono"
+)
+
+fs_arr=(
+ 8000
+ 16000
+ 24000
+ 32000
+ 48000
+)
+
+# run multichannel effects at different configs, saving only the mono channel
+error_count=0
+test_count=0
+for cmd in "${cmds[@]}"
+do
+ $cmd
+ for flags in "${flags_arr[@]}"
+ do
+ for fs in ${fs_arr[*]}
+ do
+ for chMask in {0..7}
+ do
+ adb shell $testdir/AudioPreProcessingTest $flags \
+ --i $testdir/sinesweepraw.raw --far $testdir/sinesweepraw.raw \
+ --output $testdir/sinesweep_$((chMask))_$((fs)).raw --ch_mask $chMask \
+ --fs $fs --fch 1
+
+ shell_ret=$?
+ if [ $shell_ret -ne 0 ]; then
+ echo "error shell_ret here is zero: $shell_ret"
+ ((++error_count))
+ fi
+
+
+ # single channel files should be identical to higher channel
+ # computation (first channel).
+ if [[ "$chMask" -gt 1 ]]
+ then
+ adb shell cmp $testdir/sinesweep_1_$((fs)).raw \
+ $testdir/sinesweep_$((chMask))_$((fs)).raw
+ fi
+
+ # cmp return EXIT_FAILURE on mismatch.
+ shell_ret=$?
+ if [ $shell_ret -ne 0 ]; then
+ echo "error: $shell_ret"
+ ((++error_count))
+ fi
+ ((++test_count))
+ done
+ done
+ done
+done
+
+adb shell rm -r $testdir
+echo "$test_count tests performed"
+echo "$error_count errors"
+exit $error_count
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index 4d90d98..7cda2fb 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -1822,8 +1822,7 @@
//static
void MediaPlayerService::AudioOutput::setMinBufferCount()
{
- char value[PROPERTY_VALUE_MAX];
- if (property_get("ro.kernel.qemu", value, 0)) {
+ if (property_get_bool("ro.boot.qemu", false)) {
mIsOnEmulator = true;
mMinBufferCount = 12; // to prevent systematic buffer underrun for emulator
}
diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp
index 9b3f420..4e34a26 100644
--- a/media/libstagefright/CameraSource.cpp
+++ b/media/libstagefright/CameraSource.cpp
@@ -797,7 +797,7 @@
mStartTimeUs = 0;
mNumInputBuffers = 0;
mEncoderFormat = HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED;
- mEncoderDataSpace = HAL_DATASPACE_V0_BT709;
+ mEncoderDataSpace = mBufferDataSpace = HAL_DATASPACE_V0_BT709;
if (meta) {
int64_t startTimeUs;
@@ -817,6 +817,7 @@
}
if (meta->findInt32(kKeyColorSpace, &mEncoderDataSpace)) {
ALOGI("Using encoder data space: %#x", mEncoderDataSpace);
+ mBufferDataSpace = mEncoderDataSpace;
}
}
@@ -1114,6 +1115,11 @@
(*buffer)->setObserver(this);
(*buffer)->add_ref();
(*buffer)->meta_data().setInt64(kKeyTime, frameTime);
+ if (mBufferDataSpace != mEncoderDataSpace) {
+ ALOGD("Data space updated to %x", mBufferDataSpace);
+ (*buffer)->meta_data().setInt32(kKeyColorSpace, mBufferDataSpace);
+ mEncoderDataSpace = mBufferDataSpace;
+ }
}
return OK;
}
@@ -1391,6 +1397,7 @@
// Find a available memory slot to store the buffer as VideoNativeMetadata.
sp<IMemory> data = *mMemoryBases.begin();
mMemoryBases.erase(mMemoryBases.begin());
+ mBufferDataSpace = buffer.mDataSpace;
ssize_t offset;
size_t size;
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index d99596e..b2fae96 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -4217,13 +4217,20 @@
void MPEG4Writer::Track::writeColrBox() {
ColorAspects aspects;
memset(&aspects, 0, sizeof(aspects));
+ // Color metadata may have changed.
+ sp<MetaData> meta = mSource->getFormat();
// TRICKY: using | instead of || because we want to execute all findInt32-s
- if (mMeta->findInt32(kKeyColorPrimaries, (int32_t*)&aspects.mPrimaries)
- | mMeta->findInt32(kKeyTransferFunction, (int32_t*)&aspects.mTransfer)
- | mMeta->findInt32(kKeyColorMatrix, (int32_t*)&aspects.mMatrixCoeffs)
- | mMeta->findInt32(kKeyColorRange, (int32_t*)&aspects.mRange)) {
+ if (meta->findInt32(kKeyColorPrimaries, (int32_t*)&aspects.mPrimaries)
+ | meta->findInt32(kKeyTransferFunction, (int32_t*)&aspects.mTransfer)
+ | meta->findInt32(kKeyColorMatrix, (int32_t*)&aspects.mMatrixCoeffs)
+ | meta->findInt32(kKeyColorRange, (int32_t*)&aspects.mRange)) {
int32_t primaries, transfer, coeffs;
bool fullRange;
+ ALOGV("primaries=%s transfer=%s matrix=%s range=%s",
+ asString(aspects.mPrimaries),
+ asString(aspects.mTransfer),
+ asString(aspects.mMatrixCoeffs),
+ asString(aspects.mRange));
ColorUtils::convertCodecColorAspectsToIsoAspects(
aspects, &primaries, &transfer, &coeffs, &fullRange);
mOwner->beginBox("colr");
@@ -4233,6 +4240,8 @@
mOwner->writeInt16(coeffs);
mOwner->writeInt8(int8_t(fullRange ? 0x80 : 0x0));
mOwner->endBox(); // colr
+ } else {
+ ALOGV("no color information");
}
}
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index b0b7978..4406efd 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -2244,6 +2244,8 @@
}
postPendingRepliesAndDeferredMessages(origin + ":dead");
sendErrorResponse = false;
+ } else if (!mReplyID) {
+ sendErrorResponse = false;
}
break;
}
diff --git a/media/libstagefright/MediaCodecSource.cpp b/media/libstagefright/MediaCodecSource.cpp
index bc656a2..0f7df24 100644
--- a/media/libstagefright/MediaCodecSource.cpp
+++ b/media/libstagefright/MediaCodecSource.cpp
@@ -30,6 +30,7 @@
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/ALooper.h>
#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/ColorUtils.h>
#include <media/stagefright/MediaBuffer.h>
#include <media/stagefright/MediaCodec.h>
#include <media/stagefright/MediaCodecConstants.h>
@@ -768,6 +769,26 @@
memcpy(inbuf->data(), mbuf->data(), size);
if (mIsVideo) {
+ int32_t ds = 0;
+ if (mbuf->meta_data().findInt32(kKeyColorSpace, &ds)
+ && ds != HAL_DATASPACE_UNKNOWN) {
+ android_dataspace dataspace = static_cast<android_dataspace>(ds);
+ ColorUtils::convertDataSpaceToV0(dataspace);
+ ALOGD("Updating dataspace to %x", dataspace);
+ int32_t standard = (int32_t(dataspace) & HAL_DATASPACE_STANDARD_MASK)
+ >> HAL_DATASPACE_STANDARD_SHIFT;
+ int32_t transfer = (int32_t(dataspace) & HAL_DATASPACE_TRANSFER_MASK)
+ >> HAL_DATASPACE_TRANSFER_SHIFT;
+ int32_t range = (int32_t(dataspace) & HAL_DATASPACE_RANGE_MASK)
+ >> HAL_DATASPACE_RANGE_SHIFT;
+ sp<AMessage> msg = new AMessage;
+ msg->setInt32(KEY_COLOR_STANDARD, standard);
+ msg->setInt32(KEY_COLOR_TRANSFER, transfer);
+ msg->setInt32(KEY_COLOR_RANGE, range);
+ msg->setInt32("android._dataspace", dataspace);
+ mEncoder->setParameters(msg);
+ }
+
// video encoder will release MediaBuffer when done
// with underlying data.
inbuf->meta()->setObject("mediaBufferHolder", new MediaBufferHolder(mbuf));
diff --git a/media/libstagefright/include/media/stagefright/CameraSource.h b/media/libstagefright/include/media/stagefright/CameraSource.h
index 6f0d3b5..efdfa02 100644
--- a/media/libstagefright/include/media/stagefright/CameraSource.h
+++ b/media/libstagefright/include/media/stagefright/CameraSource.h
@@ -192,6 +192,7 @@
int32_t mColorFormat;
int32_t mEncoderFormat;
int32_t mEncoderDataSpace;
+ int32_t mBufferDataSpace;
status_t mInitCheck;
sp<Camera> mCamera;
diff --git a/media/libstagefright/include/media/stagefright/MediaCodecConstants.h b/media/libstagefright/include/media/stagefright/MediaCodecConstants.h
index 6cf1ba0..9793b89 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodecConstants.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodecConstants.h
@@ -793,7 +793,7 @@
constexpr char KEY_PCM_ENCODING[] = "pcm-encoding";
constexpr char KEY_PIXEL_ASPECT_RATIO_HEIGHT[] = "sar-height";
constexpr char KEY_PIXEL_ASPECT_RATIO_WIDTH[] = "sar-width";
-constexpr char KEY_PREPEND_HEADERS_TO_SYNC_FRAMES[] = "prepend-sps-pps-to-idr-frames";
+constexpr char KEY_PREPEND_HEADER_TO_SYNC_FRAMES[] = "prepend-sps-pps-to-idr-frames";
constexpr char KEY_PRIORITY[] = "priority";
constexpr char KEY_PROFILE[] = "profile";
constexpr char KEY_PUSH_BLANK_BUFFERS_ON_STOP[] = "push-blank-buffers-on-shutdown";
diff --git a/media/libstagefright/tests/mediacodec/MediaCodecTest.cpp b/media/libstagefright/tests/mediacodec/MediaCodecTest.cpp
index 6facbd8..06e36ad 100644
--- a/media/libstagefright/tests/mediacodec/MediaCodecTest.cpp
+++ b/media/libstagefright/tests/mediacodec/MediaCodecTest.cpp
@@ -285,10 +285,11 @@
// 1) Client thread calls stop(); MediaCodec looper thread calls
// initiateShutdown(); shutdown is being handled at the component thread.
// 2) Error occurred, but the shutdown operation is still being done.
- // 3) MediaCodec looper thread handles the error.
- // 4) Client releases the codec upon the error; previous shutdown is still
+ // 3) Another error occurred during the shutdown operation.
+ // 4) MediaCodec looper thread handles the error.
+ // 5) Client releases the codec upon the error; previous shutdown is still
// going on.
- // 5) Component thread completes shutdown and posts onStopCompleted();
+ // 6) Component thread completes shutdown and posts onStopCompleted();
// Shutdown from release also completes.
static const AString kCodecName{"test.codec"};
@@ -317,6 +318,9 @@
});
ON_CALL(*mockCodec, initiateShutdown(true))
.WillByDefault([mockCodec](bool) {
+ // 2)
+ mockCodec->callback()->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
+ // 3)
mockCodec->callback()->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
});
ON_CALL(*mockCodec, initiateShutdown(false))
@@ -339,6 +343,8 @@
codec->start();
// stop() will fail because of the error
EXPECT_NE(OK, codec->stop());
+ // sleep here so that the looper thread can handle all the errors.
+ std::this_thread::sleep_for(std::chrono::milliseconds(100));
// upon receiving the error, client tries to release the codec.
codec->release();
looper->stop();
diff --git a/media/tests/SampleVideoEncoder/README.md b/media/tests/SampleVideoEncoder/README.md
new file mode 100644
index 0000000..074c939
--- /dev/null
+++ b/media/tests/SampleVideoEncoder/README.md
@@ -0,0 +1,42 @@
+# B-Frames Encoding App
+
+This is a sample android application for encoding AVC/HEVC streams with B-Frames enabled. It uses MediaRecorder APIs to record B-frames enabled video from camera2 input and MediaCodec APIs to encode reference test vector using input surface.
+
+This page describes how to get started with the Encoder App.
+
+
+# Getting Started
+
+This app uses the Gradle build system as well as Soong Build System.
+
+To build this project using Gradle build, use the "gradlew build" command or use "Import Project" in Android Studio.
+
+To build the app using Soong Build System, run the following command:
+```
+mmm frameworks/av/media/tests/SampleVideoEncoder/
+```
+
+The apk is generated at the following location:
+```
+out\target\product\sargo\testcases\SampleVideoEncoder\arm64\SampleVideoEncoder.apk
+```
+
+Command to install the apk:
+```
+adb install SampleVideoEncoder.apk
+```
+
+Command to launch the app:
+```
+adb shell am start -n "com.android.media.samplevideoencoder/com.android.media.samplevideoencoder.MainActivity"
+```
+
+After installing the app, a TextureView showing camera preview is dispalyed on one third of the screen. It also features checkboxes to select either avc/hevc and hw/sw codecs. It also has an option to select either MediaRecorder APIs or MediaCodec, along with the 'Start' button to start/stop recording.
+
+
+# Ouput
+
+The muxed ouptput video is saved in the app data at:
+```
+/storage/emulated/0/Android/data/com.android.media.samplevideoencoder/files/
+```
diff --git a/media/tests/SampleVideoEncoder/app/Android.bp b/media/tests/SampleVideoEncoder/app/Android.bp
new file mode 100644
index 0000000..3a66955
--- /dev/null
+++ b/media/tests/SampleVideoEncoder/app/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.
+ */
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_av_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_av_license"],
+}
+
+android_app {
+ name: "SampleVideoEncoder",
+
+ manifest: "src/main/AndroidManifest.xml",
+
+ srcs: ["src/**/*.java"],
+
+ sdk_version: "current",
+ min_sdk_version: "24", // N
+
+ resource_dirs: [
+ "src/main/res",
+ ],
+
+ static_libs: [
+ "androidx.annotation_annotation",
+ "androidx.appcompat_appcompat",
+ "androidx-constraintlayout_constraintlayout",
+ ],
+
+ javacflags: [
+ "-Xlint:deprecation",
+ "-Xlint:unchecked",
+ ],
+}
diff --git a/media/tests/SampleVideoEncoder/app/build.gradle b/media/tests/SampleVideoEncoder/app/build.gradle
new file mode 100644
index 0000000..cc54981
--- /dev/null
+++ b/media/tests/SampleVideoEncoder/app/build.gradle
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 30
+ buildToolsVersion "30.0.2"
+
+ defaultConfig {
+ applicationId "com.android.media.samplevideoencoder"
+ minSdkVersion 24
+ targetSdkVersion 30
+ versionCode 1
+ versionName "1.0"
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ implementation fileTree(dir: "libs", include: ["*.jar"])
+ implementation 'androidx.appcompat:appcompat:1.2.0'
+ implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
+ testImplementation 'junit:junit:4.13.1'
+ androidTestImplementation 'androidx.test:runner:1.3.0'
+ androidTestImplementation 'androidx.test.ext:junit:1.1.2'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
+}
\ No newline at end of file
diff --git a/media/tests/SampleVideoEncoder/app/src/main/AndroidManifest.xml b/media/tests/SampleVideoEncoder/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..ed668bb
--- /dev/null
+++ b/media/tests/SampleVideoEncoder/app/src/main/AndroidManifest.xml
@@ -0,0 +1,41 @@
+<?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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.media.samplevideoencoder">
+
+ <uses-permission android:name="android.permission.CAMERA"/>
+ <uses-permission android:name="android.permission.RECORD_AUDIO"/>
+
+ <application
+ android:configChanges="orientation"
+ android:screenOrientation="portrait"
+ android:allowBackup="true"
+ android:icon="@mipmap/ic_launcher"
+ android:label="@string/app_name"
+ android:roundIcon="@mipmap/ic_launcher_round"
+ android:supportsRtl="true"
+ android:theme="@style/AppTheme">
+ <activity android:name="com.android.media.samplevideoencoder.MainActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+
+</manifest>
\ No newline at end of file
diff --git a/media/tests/SampleVideoEncoder/app/src/main/java/com/android/media/samplevideoencoder/AutoFitTextureView.java b/media/tests/SampleVideoEncoder/app/src/main/java/com/android/media/samplevideoencoder/AutoFitTextureView.java
new file mode 100644
index 0000000..a3ea4c7
--- /dev/null
+++ b/media/tests/SampleVideoEncoder/app/src/main/java/com/android/media/samplevideoencoder/AutoFitTextureView.java
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+package com.android.media.samplevideoencoder;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.TextureView;
+
+public class AutoFitTextureView extends TextureView {
+
+ public AutoFitTextureView(Context context) {
+ this(context, null);
+ }
+
+ public AutoFitTextureView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public AutoFitTextureView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ public void setAspectRatio(int width, int height) {
+ if (width < 0 || height < 0) {
+ throw new IllegalArgumentException("Size cannot be negative.");
+ }
+ requestLayout();
+ }
+}
diff --git a/media/tests/SampleVideoEncoder/app/src/main/java/com/android/media/samplevideoencoder/MainActivity.java b/media/tests/SampleVideoEncoder/app/src/main/java/com/android/media/samplevideoencoder/MainActivity.java
new file mode 100644
index 0000000..33e81bb
--- /dev/null
+++ b/media/tests/SampleVideoEncoder/app/src/main/java/com/android/media/samplevideoencoder/MainActivity.java
@@ -0,0 +1,648 @@
+/*
+ * 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.
+ */
+package com.android.media.samplevideoencoder;
+
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.core.app.ActivityCompat;
+
+import android.Manifest;
+import android.app.Activity;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+
+import android.graphics.Matrix;
+import android.graphics.RectF;
+import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CameraCaptureSession;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraManager;
+import android.hardware.camera2.CameraMetadata;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.params.StreamConfigurationMap;
+import android.graphics.SurfaceTexture;
+import android.media.MediaCodecInfo;
+import android.media.MediaFormat;
+import android.media.MediaRecorder;
+
+import android.os.AsyncTask;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.view.Surface;
+import android.view.View;
+import android.view.TextureView;
+import android.widget.Button;
+import android.widget.CheckBox;
+
+import java.io.File;
+import java.io.IOException;
+
+import android.util.Log;
+import android.util.Size;
+import android.widget.RadioGroup;
+import android.widget.Toast;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+import static java.lang.Boolean.FALSE;
+import static java.lang.Boolean.TRUE;
+
+public class MainActivity extends AppCompatActivity
+ implements View.OnClickListener, ActivityCompat.OnRequestPermissionsResultCallback {
+
+ private static final String TAG = "SampleVideoEncoder";
+ private static final String[] RECORD_PERMISSIONS =
+ {Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO};
+ private static final int REQUEST_RECORD_PERMISSIONS = 1;
+ private final Semaphore mCameraOpenCloseLock = new Semaphore(1);
+ private static final int VIDEO_BITRATE = 8000000 /* 8 Mbps */;
+ private static final int VIDEO_FRAMERATE = 30;
+
+ private String mMime = MediaFormat.MIMETYPE_VIDEO_AVC;
+ private String mOutputVideoPath = null;
+
+ private final boolean mIsFrontCamera = true;
+ private boolean mIsCodecSoftware = false;
+ private boolean mIsMediaRecorder = true;
+ private boolean mIsRecording;
+
+ private AutoFitTextureView mTextureView;
+ private CameraDevice mCameraDevice;
+ private CameraCaptureSession mPreviewSession;
+ private CaptureRequest.Builder mPreviewBuilder;
+ private MediaRecorder mMediaRecorder;
+ private Size mVideoSize;
+ private Size mPreviewSize;
+
+ private Handler mBackgroundHandler;
+ private HandlerThread mBackgroundThread;
+
+ private Button mStartButton;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+
+ final RadioGroup radioGroup_mime = findViewById(R.id.radio_group_mime);
+ radioGroup_mime.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(RadioGroup group, int checkedId) {
+ if (checkedId == R.id.avc) {
+ mMime = MediaFormat.MIMETYPE_VIDEO_AVC;
+ } else {
+ mMime = MediaFormat.MIMETYPE_VIDEO_HEVC;
+ }
+ }
+ });
+
+ final RadioGroup radioGroup_codec = findViewById(R.id.radio_group_codec);
+ radioGroup_codec.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(RadioGroup group, int checkedId) {
+ mIsCodecSoftware = checkedId == R.id.sw;
+ }
+ });
+
+ final CheckBox checkBox_mr = findViewById(R.id.checkBox_media_recorder);
+ final CheckBox checkBox_mc = findViewById(R.id.checkBox_media_codec);
+ mTextureView = findViewById(R.id.texture);
+ checkBox_mr.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ boolean checked = ((CheckBox) v).isChecked();
+ if (checked) {
+ checkBox_mc.setChecked(false);
+ mIsMediaRecorder = TRUE;
+ for (int i = 0; i < radioGroup_codec.getChildCount(); i++) {
+ radioGroup_codec.getChildAt(i).setEnabled(false);
+ }
+ }
+ }
+ });
+ checkBox_mc.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ boolean checked = ((CheckBox) v).isChecked();
+ if (checked) {
+ checkBox_mr.setChecked(false);
+ mIsMediaRecorder = FALSE;
+ for (int i = 0; i < radioGroup_codec.getChildCount(); i++) {
+ radioGroup_codec.getChildAt(i).setEnabled(true);
+ }
+ }
+ }
+ });
+ mStartButton = findViewById(R.id.start_button);
+ mStartButton.setOnClickListener(this);
+ }
+
+ @Override
+ public void onClick(View v) {
+ if (v.getId() == R.id.start_button) {
+ if (mIsMediaRecorder) {
+ if (mIsRecording) {
+ stopRecordingVideo();
+ } else {
+ mStartButton.setEnabled(false);
+ startRecordingVideo();
+ }
+ } else {
+ mStartButton.setEnabled(false);
+ mOutputVideoPath = getVideoPath(MainActivity.this);
+ MediaCodecSurfaceAsync codecAsyncTask = new MediaCodecSurfaceAsync(this);
+ codecAsyncTask.execute(
+ "Encoding reference test vector with MediaCodec APIs using surface");
+ }
+ }
+ }
+
+ private static class MediaCodecSurfaceAsync extends AsyncTask<String, String, Integer> {
+
+ private final WeakReference<MainActivity> activityReference;
+
+ MediaCodecSurfaceAsync(MainActivity context) {
+ activityReference = new WeakReference<>(context);
+ }
+
+ @Override
+ protected Integer doInBackground(String... strings) {
+ MainActivity mainActivity = activityReference.get();
+ int resId = R.raw.crowd_1920x1080_25fps_4000kbps_h265;
+ int encodingStatus = 1;
+ MediaCodecSurfaceEncoder codecSurfaceEncoder =
+ new MediaCodecSurfaceEncoder(mainActivity.getApplicationContext(), resId,
+ mainActivity.mMime, mainActivity.mIsCodecSoftware,
+ mainActivity.mOutputVideoPath);
+ try {
+ encodingStatus = codecSurfaceEncoder.startEncodingSurface();
+ } catch (IOException | InterruptedException e) {
+ e.printStackTrace();
+ }
+ return encodingStatus;
+ }
+
+ @Override
+ protected void onPostExecute(Integer encodingStatus) {
+ MainActivity mainActivity = activityReference.get();
+ mainActivity.mStartButton.setEnabled(true);
+ if (encodingStatus == 0) {
+ Toast.makeText(mainActivity.getApplicationContext(), "Encoding Completed",
+ Toast.LENGTH_SHORT).show();
+ } else {
+ Toast.makeText(mainActivity.getApplicationContext(),
+ "Error occurred while " + "encoding", Toast.LENGTH_SHORT).show();
+ }
+ mainActivity.mOutputVideoPath = null;
+ super.onPostExecute(encodingStatus);
+ }
+ }
+
+ private final TextureView.SurfaceTextureListener mSurfaceTextureListener =
+ new TextureView.SurfaceTextureListener() {
+
+ @Override
+ public void onSurfaceTextureAvailable(SurfaceTexture surface, int width,
+ int height) {
+ openCamera(width, height);
+ }
+
+ @Override
+ public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width,
+ int height) {
+ configureTransform(width, height);
+ Log.v(TAG, "Keeping camera preview size fixed");
+ }
+
+ @Override
+ public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
+ return true;
+ }
+
+ @Override
+ public void onSurfaceTextureUpdated(SurfaceTexture surface) {
+ }
+ };
+
+
+ private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {
+
+ @Override
+ public void onOpened(CameraDevice cameraDevice) {
+ mCameraDevice = cameraDevice;
+ startPreview();
+ mCameraOpenCloseLock.release();
+ }
+
+ @Override
+ public void onDisconnected(CameraDevice cameraDevice) {
+ mCameraOpenCloseLock.release();
+ cameraDevice.close();
+ mCameraDevice = null;
+ }
+
+ @Override
+ public void onError(CameraDevice cameraDevice, int error) {
+ mCameraOpenCloseLock.release();
+ cameraDevice.close();
+ mCameraDevice = null;
+ Activity activity = MainActivity.this;
+ activity.finish();
+ }
+ };
+
+ private boolean shouldShowRequestPermissionRationale(String[] recordPermissions) {
+ for (String permission : recordPermissions) {
+ if (ActivityCompat.shouldShowRequestPermissionRationale(this, permission)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void requestRecordPermissions() {
+ if (!shouldShowRequestPermissionRationale(RECORD_PERMISSIONS)) {
+ ActivityCompat.requestPermissions(this, RECORD_PERMISSIONS, REQUEST_RECORD_PERMISSIONS);
+ }
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, String[] permissions,
+ int[] grantResults) {
+ if (requestCode == REQUEST_RECORD_PERMISSIONS) {
+ if (grantResults.length == RECORD_PERMISSIONS.length) {
+ for (int result : grantResults) {
+ if (result != PackageManager.PERMISSION_GRANTED) {
+ Log.e(TAG, "Permission is not granted");
+ break;
+ }
+ }
+ }
+ } else {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+ }
+ }
+
+ @SuppressWarnings("MissingPermission")
+ private void openCamera(int width, int height) {
+ if (!hasPermissionGranted(RECORD_PERMISSIONS)) {
+ Log.e(TAG, "Camera does not have permission to record video");
+ requestRecordPermissions();
+ return;
+ }
+ final Activity activity = MainActivity.this;
+ if (activity == null || activity.isFinishing()) {
+ Log.e(TAG, "Activity not found");
+ return;
+ }
+ CameraManager manager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE);
+ try {
+ Log.v(TAG, "Acquire Camera");
+ if (!mCameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) {
+ throw new RuntimeException("Timed out waiting to lock camera opening");
+ }
+ Log.d(TAG, "Camera Acquired");
+
+ String cameraId = manager.getCameraIdList()[0];
+ if (mIsFrontCamera) {
+ cameraId = manager.getCameraIdList()[1];
+ }
+
+ CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
+ StreamConfigurationMap map =
+ characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
+ mVideoSize = chooseVideoSize(map.getOutputSizes(MediaRecorder.class));
+ mPreviewSize =
+ chooseOptimalSize(map.getOutputSizes(SurfaceTexture.class), width, height,
+ mVideoSize);
+ mTextureView.setAspectRatio(mPreviewSize.getHeight(), mPreviewSize.getWidth());
+ configureTransform(width, height);
+ mMediaRecorder = new MediaRecorder();
+ manager.openCamera(cameraId, mStateCallback, null);
+ } catch (InterruptedException | CameraAccessException e) {
+ e.printStackTrace();
+ }
+ }
+
+ private void closeCamera() {
+ try {
+ mCameraOpenCloseLock.acquire();
+ closePreviewSession();
+ if (null != mCameraDevice) {
+ mCameraDevice.close();
+ mCameraDevice = null;
+ }
+ if (null != mMediaRecorder) {
+ mMediaRecorder.release();
+ mMediaRecorder = null;
+ }
+ } catch (InterruptedException e) {
+ throw new RuntimeException("Interrupted while trying to lock camera closing.");
+ } finally {
+ mCameraOpenCloseLock.release();
+ }
+ }
+
+ private static Size chooseVideoSize(Size[] choices) {
+ for (Size size : choices) {
+ if (size.getWidth() == size.getHeight() * 16 / 9 && size.getWidth() <= 1920) {
+ return size;
+ }
+ }
+ Log.e(TAG, "Couldn't find any suitable video size");
+ return choices[choices.length - 1];
+ }
+
+ private static Size chooseOptimalSize(Size[] choices, int width, int height, Size aspectRatio) {
+ List<Size> bigEnough = new ArrayList<>();
+ int w = aspectRatio.getWidth();
+ int h = aspectRatio.getHeight();
+ for (Size option : choices) {
+ if (option.getHeight() == option.getWidth() * h / w && option.getWidth() >= width &&
+ option.getHeight() >= height) {
+ bigEnough.add(option);
+ }
+ }
+
+ // Pick the smallest of those, assuming we found any
+ if (bigEnough.size() > 0) {
+ return Collections.min(bigEnough, new CompareSizesByArea());
+ } else {
+ Log.e(TAG, "Couldn't find any suitable preview size");
+ return choices[0];
+ }
+ }
+
+ private boolean hasPermissionGranted(String[] recordPermissions) {
+ for (String permission : recordPermissions) {
+ if (ActivityCompat.checkSelfPermission(MainActivity.this, permission) !=
+ PackageManager.PERMISSION_GRANTED) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ startBackgroundThread();
+ if (mTextureView.isAvailable()) {
+ openCamera(mTextureView.getWidth(), mTextureView.getHeight());
+ } else {
+ mTextureView.setSurfaceTextureListener(mSurfaceTextureListener);
+ }
+ }
+
+ @Override
+ public void onPause() {
+ closeCamera();
+ stopBackgroundThread();
+ super.onPause();
+ }
+
+ private void startBackgroundThread() {
+ mBackgroundThread = new HandlerThread("CameraBackground");
+ mBackgroundThread.start();
+ mBackgroundHandler = new Handler(mBackgroundThread.getLooper());
+ }
+
+ private void stopBackgroundThread() {
+ mBackgroundThread.quitSafely();
+ try {
+ mBackgroundThread.join();
+ mBackgroundThread = null;
+ mBackgroundHandler = null;
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+
+ private void startRecordingVideo() {
+ if (null == mCameraDevice || !mTextureView.isAvailable() || null == mPreviewSize) {
+ Toast.makeText(MainActivity.this, "Cannot start recording.", Toast.LENGTH_SHORT).show();
+ Log.e(TAG, "Cannot start recording.");
+ return;
+ }
+ try {
+ closePreviewSession();
+ setUpMediaRecorder();
+ SurfaceTexture texture = mTextureView.getSurfaceTexture();
+ assert texture != null;
+ texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
+ mPreviewBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
+ List<Surface> surfaces = new ArrayList<>();
+
+ // Set up Surface for the camera preview
+ Surface previewSurface = new Surface(texture);
+ surfaces.add(previewSurface);
+ mPreviewBuilder.addTarget(previewSurface);
+
+ // Set up Surface for the MediaRecorder
+ Surface recorderSurface = mMediaRecorder.getSurface();
+ surfaces.add(recorderSurface);
+ mPreviewBuilder.addTarget(recorderSurface);
+
+ //Start a capture session
+ mCameraDevice.createCaptureSession(surfaces, new CameraCaptureSession.StateCallback() {
+
+ @Override
+ public void onConfigured(CameraCaptureSession session) {
+ mPreviewSession = session;
+ updatePreview();
+ MainActivity.this.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ mIsRecording = true;
+ mMediaRecorder.start();
+ mStartButton.setText(R.string.stop);
+ mStartButton.setEnabled(true);
+ }
+ });
+ }
+
+ @Override
+ public void onConfigureFailed(CameraCaptureSession session) {
+ Log.e(TAG, "Failed to configure. Cannot start Recording");
+ }
+ }, mBackgroundHandler);
+ } catch (CameraAccessException e) {
+ e.printStackTrace();
+ }
+ }
+
+ private void setUpMediaRecorder() {
+ final Activity activity = MainActivity.this;
+ if (activity == null) {
+ Toast.makeText(MainActivity.this, "Error occurred while setting up the MediaRecorder",
+ Toast.LENGTH_SHORT).show();
+ Log.e(TAG, "Error occurred while setting up the MediaRecorder");
+ return;
+ }
+ try {
+ mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
+ mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
+ mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
+ } catch (IllegalStateException e) {
+ e.printStackTrace();
+ }
+ if (mOutputVideoPath == null) {
+ mOutputVideoPath = getVideoPath(MainActivity.this);
+ }
+ mMediaRecorder.setOutputFile(mOutputVideoPath);
+ mMediaRecorder.setVideoEncodingBitRate(VIDEO_BITRATE);
+ mMediaRecorder.setVideoFrameRate(VIDEO_FRAMERATE);
+ mMediaRecorder.setVideoSize(mVideoSize.getWidth(), mVideoSize.getHeight());
+ mMediaRecorder.setOrientationHint(270);
+ if (mMime.equals(MediaFormat.MIMETYPE_VIDEO_HEVC)) {
+ mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.HEVC);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ mMediaRecorder.setVideoEncodingProfileLevel(
+ MediaCodecInfo.CodecProfileLevel.HEVCProfileMain,
+ MediaCodecInfo.CodecProfileLevel.HEVCMainTierLevel4);
+ }
+ } else {
+ mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ mMediaRecorder.setVideoEncodingProfileLevel(
+ MediaCodecInfo.CodecProfileLevel.AVCProfileMain,
+ MediaCodecInfo.CodecProfileLevel.AVCLevel4);
+ }
+ }
+ mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
+ try {
+ mMediaRecorder.prepare();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ private String getVideoPath(Activity activity) {
+ File dir = activity.getApplicationContext().getExternalFilesDir(null);
+ if (dir == null) {
+ Log.e(TAG, "Cannot get external directory path to save output video");
+ return null;
+ }
+ String videoPath = dir.getAbsolutePath() + "/Video-" + System.currentTimeMillis() + ".mp4";
+ Log.d(TAG, "Output video is saved at: " + videoPath);
+ return videoPath;
+ }
+
+ private void closePreviewSession() {
+ if (mPreviewSession != null) {
+ mPreviewSession.close();
+ mPreviewSession = null;
+ }
+ }
+
+ private void stopRecordingVideo() {
+ mIsRecording = false;
+ mStartButton.setText(R.string.start);
+ mMediaRecorder.stop();
+ mMediaRecorder.reset();
+ Toast.makeText(MainActivity.this, "Recording Finished", Toast.LENGTH_SHORT).show();
+ mOutputVideoPath = null;
+ startPreview();
+ }
+
+ private void startPreview() {
+ if (null == mCameraDevice || !mTextureView.isAvailable() || null == mPreviewSize) {
+ return;
+ }
+ try {
+ closePreviewSession();
+ SurfaceTexture texture = mTextureView.getSurfaceTexture();
+ assert texture != null;
+ texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
+ mPreviewBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+
+ Surface previewSurface = new Surface(texture);
+ mPreviewBuilder.addTarget(previewSurface);
+
+ mCameraDevice.createCaptureSession(Collections.singletonList(previewSurface),
+ new CameraCaptureSession.StateCallback() {
+
+ @Override
+ public void onConfigured(CameraCaptureSession session) {
+ mPreviewSession = session;
+ updatePreview();
+ }
+
+ @Override
+ public void onConfigureFailed(CameraCaptureSession session) {
+ Toast.makeText(MainActivity.this,
+ "Configure Failed; Cannot start " + "preview",
+ Toast.LENGTH_SHORT).show();
+ Log.e(TAG, "Configure failed; Cannot start preview");
+ }
+ }, mBackgroundHandler);
+ } catch (CameraAccessException e) {
+ e.printStackTrace();
+ }
+ }
+
+ private void updatePreview() {
+ if (mCameraDevice == null) {
+ Toast.makeText(MainActivity.this, "Camera not found; Cannot update " + "preview",
+ Toast.LENGTH_SHORT).show();
+ Log.e(TAG, "Camera not found; Cannot update preview");
+ return;
+ }
+ try {
+ mPreviewBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);
+ HandlerThread thread = new HandlerThread("Camera preview");
+ thread.start();
+ mPreviewSession.setRepeatingRequest(mPreviewBuilder.build(), null, mBackgroundHandler);
+ } catch (CameraAccessException e) {
+ e.printStackTrace();
+ }
+ }
+
+ private void configureTransform(int viewWidth, int viewHeight) {
+ Activity activity = MainActivity.this;
+ if (null == mTextureView || null == mPreviewSize || null == activity) {
+ return;
+ }
+ Matrix matrix = new Matrix();
+ RectF viewRect = new RectF(0, 0, viewWidth, viewHeight);
+ RectF bufferRect = new RectF(0, 0, mPreviewSize.getHeight(), mPreviewSize.getWidth());
+ float centerX = viewRect.centerX();
+ float centerY = viewRect.centerY();
+ bufferRect.offset(centerX - bufferRect.centerX(), centerY - bufferRect.centerY());
+ matrix.setRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.FILL);
+ float scale = Math.max((float) viewHeight / mPreviewSize.getHeight(),
+ (float) viewWidth / mPreviewSize.getWidth());
+ matrix.postScale(scale, scale, centerX, centerY);
+ mTextureView.setTransform(matrix);
+ }
+
+ static class CompareSizesByArea implements Comparator<Size> {
+ @Override
+ public int compare(Size lhs, Size rhs) {
+ return Long.signum((long) lhs.getWidth() * lhs.getHeight() -
+ (long) rhs.getWidth() * rhs.getHeight());
+ }
+ }
+}
diff --git a/media/tests/SampleVideoEncoder/app/src/main/java/com/android/media/samplevideoencoder/MediaCodecBase.java b/media/tests/SampleVideoEncoder/app/src/main/java/com/android/media/samplevideoencoder/MediaCodecBase.java
new file mode 100644
index 0000000..88ce73b
--- /dev/null
+++ b/media/tests/SampleVideoEncoder/app/src/main/java/com/android/media/samplevideoencoder/MediaCodecBase.java
@@ -0,0 +1,193 @@
+/*
+ * 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.
+ */
+package com.android.media.samplevideoencoder;
+
+import android.media.MediaCodec;
+import android.media.MediaCodecInfo;
+import android.media.MediaCodecList;
+import android.media.MediaFormat;
+import android.os.Build;
+import android.util.Log;
+import android.util.Pair;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+class CodecAsyncHandler extends MediaCodec.Callback {
+ private static final String TAG = CodecAsyncHandler.class.getSimpleName();
+ private final Lock mLock = new ReentrantLock();
+ private final Condition mCondition = mLock.newCondition();
+ private final LinkedList<Pair<Integer, MediaCodec.BufferInfo>> mCbInputQueue;
+ private final LinkedList<Pair<Integer, MediaCodec.BufferInfo>> mCbOutputQueue;
+ private volatile boolean mSignalledError;
+
+ CodecAsyncHandler() {
+ mCbInputQueue = new LinkedList<>();
+ mCbOutputQueue = new LinkedList<>();
+ mSignalledError = false;
+ }
+
+ void clearQueues() {
+ mLock.lock();
+ mCbInputQueue.clear();
+ mCbOutputQueue.clear();
+ mLock.unlock();
+ }
+
+ void resetContext() {
+ clearQueues();
+ mSignalledError = false;
+ }
+
+ @Override
+ public void onInputBufferAvailable(MediaCodec codec, int bufferIndex) {
+ mLock.lock();
+ mCbInputQueue.add(new Pair<>(bufferIndex, (MediaCodec.BufferInfo) null));
+ mCondition.signalAll();
+ mLock.unlock();
+ }
+
+ @Override
+ public void onOutputBufferAvailable(MediaCodec codec, int bufferIndex,
+ MediaCodec.BufferInfo info) {
+ mLock.lock();
+ mCbOutputQueue.add(new Pair<>(bufferIndex, info));
+ mCondition.signalAll();
+ mLock.unlock();
+ }
+
+ @Override
+ public void onError(MediaCodec codec, MediaCodec.CodecException e) {
+ mLock.lock();
+ mSignalledError = true;
+ mCondition.signalAll();
+ mLock.unlock();
+ Log.e(TAG, "Received media codec error : " + e.getMessage());
+ }
+
+ @Override
+ public void onOutputFormatChanged(MediaCodec codec, MediaFormat format) {
+ Log.i(TAG, "Output format changed: " + format.toString());
+ }
+
+ void setCallBack(MediaCodec codec, boolean isCodecInAsyncMode) {
+ if (isCodecInAsyncMode) {
+ codec.setCallback(this);
+ }
+ }
+
+ Pair<Integer, MediaCodec.BufferInfo> getOutput() throws InterruptedException {
+ Pair<Integer, MediaCodec.BufferInfo> element = null;
+ mLock.lock();
+ while (!mSignalledError) {
+ if (mCbOutputQueue.isEmpty()) {
+ mCondition.await();
+ } else {
+ element = mCbOutputQueue.remove(0);
+ break;
+ }
+ }
+ mLock.unlock();
+ return element;
+ }
+
+ Pair<Integer, MediaCodec.BufferInfo> getWork() throws InterruptedException {
+ Pair<Integer, MediaCodec.BufferInfo> element = null;
+ mLock.lock();
+ while (!mSignalledError) {
+ if (mCbInputQueue.isEmpty() && mCbOutputQueue.isEmpty()) {
+ mCondition.await();
+ } else {
+ if (!mCbOutputQueue.isEmpty()) {
+ element = mCbOutputQueue.remove(0);
+ break;
+ }
+ if (!mCbInputQueue.isEmpty()) {
+ element = mCbInputQueue.remove(0);
+ break;
+ }
+ }
+ }
+ mLock.unlock();
+ return element;
+ }
+
+ boolean hasSeenError() {
+ return mSignalledError;
+ }
+}
+
+abstract public class MediaCodecBase {
+ static ArrayList<String> selectCodecs(String mime, ArrayList<MediaFormat> formats,
+ String[] features, boolean isEncoder,
+ boolean isSoftware) {
+
+ MediaCodecList codecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
+ MediaCodecInfo[] codecInfos = codecList.getCodecInfos();
+ ArrayList<String> listOfCodecs = new ArrayList<>();
+ for (MediaCodecInfo codecInfo : codecInfos) {
+ if (isEncoder) {
+ if (!codecInfo.isEncoder()) continue;
+ } else {
+ if (codecInfo.isEncoder()) continue;
+ }
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && codecInfo.isAlias()) continue;
+ String[] types = codecInfo.getSupportedTypes();
+ for (String type : types) {
+ if (type.equalsIgnoreCase(mime)) {
+ boolean isOk = true;
+ MediaCodecInfo.CodecCapabilities codecCapabilities =
+ codecInfo.getCapabilitiesForType(type);
+ if (formats != null) {
+ for (MediaFormat format : formats) {
+ if (!codecCapabilities.isFormatSupported(format)) {
+ isOk = false;
+ break;
+ }
+ }
+ }
+ if (features != null) {
+ for (String feature : features) {
+ if (!codecCapabilities.isFeatureSupported(feature)) {
+ isOk = false;
+ break;
+ }
+ }
+ }
+ if (isSoftware) {
+ if (codecInfo.getName().contains("software") ||
+ codecInfo.getName().contains("android") ||
+ codecInfo.getName().contains("google")) {
+ if (isOk) listOfCodecs.add(codecInfo.getName());
+ }
+ } else {
+ if (codecInfo.getName().contains("software") ||
+ codecInfo.getName().contains("android") ||
+ codecInfo.getName().contains("google")) {
+ continue;
+ } else {
+ if (isOk) listOfCodecs.add(codecInfo.getName());
+ }
+ }
+ }
+ }
+ }
+ return listOfCodecs;
+ }
+}
diff --git a/media/tests/SampleVideoEncoder/app/src/main/java/com/android/media/samplevideoencoder/MediaCodecSurfaceEncoder.java b/media/tests/SampleVideoEncoder/app/src/main/java/com/android/media/samplevideoencoder/MediaCodecSurfaceEncoder.java
new file mode 100644
index 0000000..146a475
--- /dev/null
+++ b/media/tests/SampleVideoEncoder/app/src/main/java/com/android/media/samplevideoencoder/MediaCodecSurfaceEncoder.java
@@ -0,0 +1,356 @@
+/*
+ * 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.
+ */
+
+package com.android.media.samplevideoencoder;
+
+import android.content.Context;
+import android.content.res.AssetFileDescriptor;
+import android.media.MediaCodec;
+import android.media.MediaCodecInfo;
+import android.media.MediaExtractor;
+import android.media.MediaFormat;
+import android.media.MediaMuxer;
+import android.os.Build;
+import android.util.Log;
+import android.util.Pair;
+import android.view.Surface;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+
+public class MediaCodecSurfaceEncoder {
+ private static final String TAG = MediaCodecSurfaceEncoder.class.getSimpleName();
+
+ private static final boolean DEBUG = false;
+ private static final int VIDEO_BITRATE = 8000000 /*8 Mbps*/;
+ private static final int VIDEO_FRAMERATE = 30;
+ private final Context mActivityContext;
+ private final int mResID;
+ private final int mMaxBFrames;
+ private final String mMime;
+ private final String mOutputPath;
+ private int mTrackID = -1;
+
+ private Surface mSurface;
+ private MediaExtractor mExtractor;
+ private MediaCodec mDecoder;
+ private MediaCodec mEncoder;
+ private MediaMuxer mMuxer;
+
+ private final boolean mIsCodecSoftware;
+ private boolean mSawDecInputEOS;
+ private boolean mSawDecOutputEOS;
+ private boolean mSawEncOutputEOS;
+ private int mDecOutputCount;
+ private int mEncOutputCount;
+
+ private final CodecAsyncHandler mAsyncHandleEncoder = new CodecAsyncHandler();
+ private final CodecAsyncHandler mAsyncHandleDecoder = new CodecAsyncHandler();
+
+ public MediaCodecSurfaceEncoder(Context context, int resId, String mime, boolean isSoftware,
+ String outputPath, int maxBFrames) {
+ mActivityContext = context;
+ mResID = resId;
+ mMime = mime;
+ mIsCodecSoftware = isSoftware;
+ mOutputPath = outputPath;
+ mMaxBFrames = maxBFrames;
+ }
+
+ public MediaCodecSurfaceEncoder(Context context, int resId, String mime, boolean isSoftware,
+ String outputPath) {
+ // Default value of MediaFormat.KEY_MAX_B_FRAMES is set to 1, if not passed as a parameter.
+ this(context, resId, mime, isSoftware, outputPath, 1);
+ }
+
+ public int startEncodingSurface() throws IOException, InterruptedException {
+ MediaFormat decoderFormat = setUpSource();
+ if (decoderFormat == null) {
+ return -1;
+ }
+
+ String decoderMime = decoderFormat.getString(MediaFormat.KEY_MIME);
+ ArrayList<String> listOfDeocders =
+ MediaCodecBase.selectCodecs(decoderMime, null, null, false, mIsCodecSoftware);
+ if (listOfDeocders.isEmpty()) {
+ Log.e(TAG, "No suitable decoder found for mime: " + decoderMime);
+ return -1;
+ }
+ mDecoder = MediaCodec.createByCodecName(listOfDeocders.get(0));
+
+ MediaFormat encoderFormat = setUpEncoderFormat(decoderFormat);
+ ArrayList<String> listOfEncoders =
+ MediaCodecBase.selectCodecs(mMime, null, null, true, mIsCodecSoftware);
+ if (listOfEncoders.isEmpty()) {
+ Log.e(TAG, "No suitable encoder found for mime: " + mMime);
+ return -1;
+ }
+
+ boolean muxOutput = true;
+ for (String encoder : listOfEncoders) {
+ mEncoder = MediaCodec.createByCodecName(encoder);
+ mExtractor.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
+ if (muxOutput) {
+ int muxerFormat = MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4;
+ mMuxer = new MediaMuxer(mOutputPath, muxerFormat);
+ }
+ configureCodec(decoderFormat, encoderFormat);
+ mEncoder.start();
+ mDecoder.start();
+ doWork(Integer.MAX_VALUE);
+ queueEOS();
+ waitForAllEncoderOutputs();
+ if (muxOutput) {
+ if (mTrackID != -1) {
+ mMuxer.stop();
+ mTrackID = -1;
+ }
+ if (mMuxer != null) {
+ mMuxer.release();
+ mMuxer = null;
+ }
+ }
+ mDecoder.reset();
+ mEncoder.reset();
+ mSurface.release();
+ mSurface = null;
+ }
+
+ mEncoder.release();
+ mDecoder.release();
+ mExtractor.release();
+ return 0;
+ }
+
+ private MediaFormat setUpSource() throws IOException {
+ mExtractor = new MediaExtractor();
+ AssetFileDescriptor fd = mActivityContext.getResources().openRawResourceFd(mResID);
+ mExtractor.setDataSource(fd.getFileDescriptor(), fd.getStartOffset(), fd.getLength());
+ for (int trackID = 0; trackID < mExtractor.getTrackCount(); trackID++) {
+ MediaFormat format = mExtractor.getTrackFormat(trackID);
+ String mime = format.getString(MediaFormat.KEY_MIME);
+ if (mime.startsWith("video/")) {
+ mExtractor.selectTrack(trackID);
+ format.setInteger(MediaFormat.KEY_COLOR_FORMAT,
+ MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible);
+ return format;
+ }
+ }
+ mExtractor.release();
+ return null;
+ }
+
+ private MediaFormat setUpEncoderFormat(MediaFormat decoderFormat) {
+ MediaFormat encoderFormat = new MediaFormat();
+ encoderFormat.setString(MediaFormat.KEY_MIME, mMime);
+ encoderFormat
+ .setInteger(MediaFormat.KEY_WIDTH, decoderFormat.getInteger(MediaFormat.KEY_WIDTH));
+ encoderFormat.setInteger(MediaFormat.KEY_HEIGHT,
+ decoderFormat.getInteger(MediaFormat.KEY_HEIGHT));
+ encoderFormat.setInteger(MediaFormat.KEY_FRAME_RATE, VIDEO_FRAMERATE);
+ encoderFormat.setInteger(MediaFormat.KEY_BIT_RATE, VIDEO_BITRATE);
+ encoderFormat.setFloat(MediaFormat.KEY_I_FRAME_INTERVAL, 1.0f);
+ encoderFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT,
+ MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
+ if (mMime.equals(MediaFormat.MIMETYPE_VIDEO_HEVC)) {
+ encoderFormat.setInteger(MediaFormat.KEY_PROFILE,
+ MediaCodecInfo.CodecProfileLevel.HEVCProfileMain);
+ encoderFormat.setInteger(MediaFormat.KEY_LEVEL,
+ MediaCodecInfo.CodecProfileLevel.HEVCMainTierLevel4);
+ } else {
+ encoderFormat.setInteger(MediaFormat.KEY_PROFILE,
+ MediaCodecInfo.CodecProfileLevel.AVCProfileMain);
+ encoderFormat
+ .setInteger(MediaFormat.KEY_LEVEL, MediaCodecInfo.CodecProfileLevel.AVCLevel4);
+ }
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+ encoderFormat.setInteger(MediaFormat.KEY_MAX_B_FRAMES, mMaxBFrames);
+ } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ encoderFormat.setInteger(MediaFormat.KEY_LATENCY, 1);
+ }
+ return encoderFormat;
+ }
+
+ private void resetContext() {
+ mAsyncHandleDecoder.resetContext();
+ mAsyncHandleEncoder.resetContext();
+ mSawDecInputEOS = false;
+ mSawDecOutputEOS = false;
+ mSawEncOutputEOS = false;
+ mDecOutputCount = 0;
+ mEncOutputCount = 0;
+ }
+
+ private void configureCodec(MediaFormat decFormat, MediaFormat encFormat) {
+ resetContext();
+ mAsyncHandleEncoder.setCallBack(mEncoder, true);
+ mEncoder.configure(encFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
+ mSurface = mEncoder.createInputSurface();
+ if (!mSurface.isValid()) {
+ Log.e(TAG, "Surface is not valid");
+ return;
+ }
+ mAsyncHandleDecoder.setCallBack(mDecoder, true);
+ mDecoder.configure(decFormat, mSurface, null, 0);
+ Log.d(TAG, "Codec configured");
+ if (DEBUG) {
+ Log.d(TAG, "Encoder Output format: " + mEncoder.getOutputFormat());
+ }
+ }
+
+ private void dequeueDecoderOutput(int bufferIndex, MediaCodec.BufferInfo info) {
+ if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+ mSawDecOutputEOS = true;
+ }
+ if (DEBUG) {
+ Log.d(TAG,
+ "output: id: " + bufferIndex + " flags: " + info.flags + " size: " + info.size +
+ " timestamp: " + info.presentationTimeUs);
+ }
+ if (info.size > 0 && (info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0) {
+ mDecOutputCount++;
+ }
+ mDecoder.releaseOutputBuffer(bufferIndex, mSurface != null);
+ }
+
+ private void enqueueDecoderInput(int bufferIndex) {
+ ByteBuffer inputBuffer = mDecoder.getInputBuffer(bufferIndex);
+ int size = mExtractor.readSampleData(inputBuffer, 0);
+ if (size < 0) {
+ enqueueDecoderEOS(bufferIndex);
+ } else {
+ long pts = mExtractor.getSampleTime();
+ int extractorFlags = mExtractor.getSampleFlags();
+ int codecFlags = 0;
+ if ((extractorFlags & MediaExtractor.SAMPLE_FLAG_SYNC) != 0) {
+ codecFlags |= MediaCodec.BUFFER_FLAG_KEY_FRAME;
+ }
+ if ((extractorFlags & MediaExtractor.SAMPLE_FLAG_PARTIAL_FRAME) != 0) {
+ codecFlags |= MediaCodec.BUFFER_FLAG_PARTIAL_FRAME;
+ }
+ if (!mExtractor.advance()) {
+ codecFlags |= MediaCodec.BUFFER_FLAG_END_OF_STREAM;
+ mSawDecInputEOS = true;
+ }
+ if (DEBUG) {
+ Log.d(TAG, "input: id: " + bufferIndex + " size: " + size + " pts: " + pts +
+ " flags: " + codecFlags);
+ }
+ mDecoder.queueInputBuffer(bufferIndex, 0, size, pts, codecFlags);
+ }
+ }
+
+ private void doWork(int frameLimit) throws InterruptedException {
+ int frameCount = 0;
+ while (!hasSeenError() && !mSawDecInputEOS && frameCount < frameLimit) {
+ Pair<Integer, MediaCodec.BufferInfo> element = mAsyncHandleDecoder.getWork();
+ if (element != null) {
+ int bufferID = element.first;
+ MediaCodec.BufferInfo info = element.second;
+ if (info != null) {
+ // <id, info> corresponds to output callback.
+ dequeueDecoderOutput(bufferID, info);
+ } else {
+ // <id, null> corresponds to input callback.
+ enqueueDecoderInput(bufferID);
+ frameCount++;
+ }
+ }
+ // check decoder EOS
+ if (mSawDecOutputEOS) mEncoder.signalEndOfInputStream();
+ // encoder output
+ if (mDecOutputCount - mEncOutputCount > mMaxBFrames) {
+ tryEncoderOutput();
+ }
+ }
+ }
+
+ private void queueEOS() throws InterruptedException {
+ while (!mAsyncHandleDecoder.hasSeenError() && !mSawDecInputEOS) {
+ Pair<Integer, MediaCodec.BufferInfo> element = mAsyncHandleDecoder.getWork();
+ if (element != null) {
+ int bufferID = element.first;
+ MediaCodec.BufferInfo info = element.second;
+ if (info != null) {
+ dequeueDecoderOutput(bufferID, info);
+ } else {
+ enqueueDecoderEOS(element.first);
+ }
+ }
+ }
+
+ while (!hasSeenError() && !mSawDecOutputEOS) {
+ Pair<Integer, MediaCodec.BufferInfo> decOp = mAsyncHandleDecoder.getOutput();
+ if (decOp != null) dequeueDecoderOutput(decOp.first, decOp.second);
+ if (mSawDecOutputEOS) mEncoder.signalEndOfInputStream();
+ if (mDecOutputCount - mEncOutputCount > mMaxBFrames) {
+ tryEncoderOutput();
+ }
+ }
+ }
+
+ private void tryEncoderOutput() throws InterruptedException {
+ if (!hasSeenError() && !mSawEncOutputEOS) {
+ Pair<Integer, MediaCodec.BufferInfo> element = mAsyncHandleEncoder.getOutput();
+ if (element != null) {
+ dequeueEncoderOutput(element.first, element.second);
+ }
+ }
+ }
+
+ private void waitForAllEncoderOutputs() throws InterruptedException {
+ while (!hasSeenError() && !mSawEncOutputEOS) {
+ tryEncoderOutput();
+ }
+ }
+
+ private void enqueueDecoderEOS(int bufferIndex) {
+ if (!mSawDecInputEOS) {
+ mDecoder.queueInputBuffer(bufferIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
+ mSawDecInputEOS = true;
+ Log.d(TAG, "Queued End of Stream");
+ }
+ }
+
+ private void dequeueEncoderOutput(int bufferIndex, MediaCodec.BufferInfo info) {
+ if (DEBUG) {
+ Log.d(TAG, "encoder output: id: " + bufferIndex + " flags: " + info.flags + " size: " +
+ info.size + " timestamp: " + info.presentationTimeUs);
+ }
+ if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+ mSawEncOutputEOS = true;
+ }
+ if (info.size > 0) {
+ ByteBuffer buf = mEncoder.getOutputBuffer(bufferIndex);
+ if (mMuxer != null) {
+ if (mTrackID == -1) {
+ mTrackID = mMuxer.addTrack(mEncoder.getOutputFormat());
+ mMuxer.start();
+ }
+ mMuxer.writeSampleData(mTrackID, buf, info);
+ }
+ if ((info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0) {
+ mEncOutputCount++;
+ }
+ }
+ mEncoder.releaseOutputBuffer(bufferIndex, false);
+ }
+
+ private boolean hasSeenError() {
+ return mAsyncHandleDecoder.hasSeenError() || mAsyncHandleEncoder.hasSeenError();
+ }
+}
diff --git a/media/tests/SampleVideoEncoder/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/media/tests/SampleVideoEncoder/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
new file mode 100644
index 0000000..2b068d1
--- /dev/null
+++ b/media/tests/SampleVideoEncoder/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt"
+ android:width="108dp"
+ android:height="108dp"
+ android:viewportWidth="108"
+ android:viewportHeight="108">
+ <path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
+ <aapt:attr name="android:fillColor">
+ <gradient
+ android:endX="85.84757"
+ android:endY="92.4963"
+ android:startX="42.9492"
+ android:startY="49.59793"
+ android:type="linear">
+ <item
+ android:color="#44000000"
+ android:offset="0.0" />
+ <item
+ android:color="#00000000"
+ android:offset="1.0" />
+ </gradient>
+ </aapt:attr>
+ </path>
+ <path
+ android:fillColor="#FFFFFF"
+ android:fillType="nonZero"
+ android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
+ android:strokeWidth="1"
+ android:strokeColor="#00000000" />
+</vector>
\ No newline at end of file
diff --git a/media/tests/SampleVideoEncoder/app/src/main/res/drawable/ic_launcher_background.xml b/media/tests/SampleVideoEncoder/app/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 0000000..07d5da9
--- /dev/null
+++ b/media/tests/SampleVideoEncoder/app/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,170 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="108dp"
+ android:height="108dp"
+ android:viewportWidth="108"
+ android:viewportHeight="108">
+ <path
+ android:fillColor="#3DDC84"
+ android:pathData="M0,0h108v108h-108z" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M9,0L9,108"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M19,0L19,108"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M29,0L29,108"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M39,0L39,108"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M49,0L49,108"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M59,0L59,108"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M69,0L69,108"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M79,0L79,108"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M89,0L89,108"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M99,0L99,108"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,9L108,9"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,19L108,19"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,29L108,29"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,39L108,39"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,49L108,49"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,59L108,59"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,69L108,69"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,79L108,79"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,89L108,89"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,99L108,99"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M19,29L89,29"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M19,39L89,39"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M19,49L89,49"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M19,59L89,59"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M19,69L89,69"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M19,79L89,79"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M29,19L29,89"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M39,19L39,89"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M49,19L49,89"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M59,19L59,89"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M69,19L69,89"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M79,19L79,89"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+</vector>
diff --git a/media/tests/SampleVideoEncoder/app/src/main/res/layout/activity_main.xml b/media/tests/SampleVideoEncoder/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..164e02a
--- /dev/null
+++ b/media/tests/SampleVideoEncoder/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="center"
+ tools:context="com.android.media.samplevideoencoder.MainActivity">
+
+ <com.android.media.samplevideoencoder.AutoFitTextureView
+ android:id="@+id/texture"
+ android:layout_width="wrap_content"
+ android:layout_height="300dp"
+ android:layout_alignParentStart="true"
+ android:layout_alignParentTop="true"
+ android:layout_marginBottom="16dp"
+ android:gravity="center"
+ app:layout_constraintBottom_toTopOf="@+id/checkBox_media_recorder"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent" />
+
+ <CheckBox
+ android:id="@+id/checkBox_media_recorder"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="90dp"
+ android:layout_marginTop="10dp"
+ android:fontFamily="sans-serif-medium"
+ android:text="@string/media_recorder"
+ android:textAppearance="@style/TextAppearance.AppCompat.Large"
+ android:textStyle="normal"
+ android:checked="true"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/texture"/>
+
+ <CheckBox
+ android:id="@+id/checkBox_media_codec"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="90dp"
+ android:fontFamily="sans-serif-medium"
+ android:text="@string/media_codec"
+ android:textAppearance="@style/TextAppearance.AppCompat.Large"
+ android:textStyle="normal"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/checkBox_media_recorder" />
+
+ <RadioGroup
+ android:id="@+id/radio_group_mime"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="40dp"
+ android:layout_marginTop="10dp"
+ android:orientation="vertical"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintBottom_toTopOf="@+id/frameLayout2"
+ app:layout_constraintTop_toBottomOf="@+id/checkBox_media_codec">
+
+ <RadioButton
+ android:id="@+id/avc"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:checked="true"
+ android:text="@string/avc" />
+
+ <RadioButton
+ android:id="@+id/hevc"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/hevc" />
+ </RadioGroup>
+
+ <RadioGroup
+ android:id="@+id/radio_group_codec"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="10dp"
+ android:layout_marginEnd="40dp"
+ android:orientation="vertical"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/checkBox_media_codec">
+
+ <RadioButton
+ android:id="@+id/hw"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:checked="true"
+ android:enabled="false"
+ android:text="@string/hardware" />
+
+ <RadioButton
+ android:id="@+id/sw"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:enabled="false"
+ android:text="@string/software" />
+ </RadioGroup>
+
+ <FrameLayout
+ android:id="@+id/frameLayout2"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/radio_group_mime"
+ android:layout_alignParentStart="true"
+ android:layout_alignParentBottom="true"
+ android:layout_marginTop="10dp"
+ android:background="@color/colorPrimary"
+ app:layout_constraintTop_toBottomOf="@+id/radio_group_mime"
+ tools:layout_editor_absoluteX="80dp">
+
+ <Button
+ android:id="@+id/start_button"
+ android:layout_width="108dp"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:gravity="center"
+ android:text="@string/start_button"
+ tools:layout_editor_absoluteX="155dp"
+ tools:layout_editor_absoluteY="455dp" />
+
+ </FrameLayout>
+
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/media/tests/SampleVideoEncoder/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/media/tests/SampleVideoEncoder/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000..eca70cf
--- /dev/null
+++ b/media/tests/SampleVideoEncoder/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+ <background android:drawable="@drawable/ic_launcher_background" />
+ <foreground android:drawable="@drawable/ic_launcher_foreground" />
+</adaptive-icon>
\ No newline at end of file
diff --git a/media/tests/SampleVideoEncoder/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/media/tests/SampleVideoEncoder/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000..eca70cf
--- /dev/null
+++ b/media/tests/SampleVideoEncoder/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+ <background android:drawable="@drawable/ic_launcher_background" />
+ <foreground android:drawable="@drawable/ic_launcher_foreground" />
+</adaptive-icon>
\ No newline at end of file
diff --git a/media/tests/SampleVideoEncoder/app/src/main/res/raw/crowd_1920x1080_25fps_4000kbps_h265.mp4 b/media/tests/SampleVideoEncoder/app/src/main/res/raw/crowd_1920x1080_25fps_4000kbps_h265.mp4
new file mode 100644
index 0000000..6204008
--- /dev/null
+++ b/media/tests/SampleVideoEncoder/app/src/main/res/raw/crowd_1920x1080_25fps_4000kbps_h265.mp4
Binary files differ
diff --git a/media/tests/SampleVideoEncoder/app/src/main/res/values/colors.xml b/media/tests/SampleVideoEncoder/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..4faecfa
--- /dev/null
+++ b/media/tests/SampleVideoEncoder/app/src/main/res/values/colors.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <color name="colorPrimary">#6200EE</color>
+ <color name="colorPrimaryDark">#3700B3</color>
+ <color name="colorAccent">#03DAC5</color>
+</resources>
\ No newline at end of file
diff --git a/media/tests/SampleVideoEncoder/app/src/main/res/values/strings.xml b/media/tests/SampleVideoEncoder/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..f825a7f
--- /dev/null
+++ b/media/tests/SampleVideoEncoder/app/src/main/res/values/strings.xml
@@ -0,0 +1,13 @@
+<resources>
+ <string name="app_name">SampleVideoEncoder</string>
+ <string name="media_recorder">MediaRecorder</string>
+ <string name="media_codec">MediaCodec</string>
+ <string name="start_button">Start</string>
+ <string name="stop">Stop</string>
+ <string name="start">Start</string>
+ <string name="avc">AVC</string>
+ <string name="hevc">HEVC</string>
+ <string name="hardware">H/W</string>
+ <string name="software">S/W</string>
+
+</resources>
\ No newline at end of file
diff --git a/media/tests/SampleVideoEncoder/app/src/main/res/values/styles.xml b/media/tests/SampleVideoEncoder/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..fac9291
--- /dev/null
+++ b/media/tests/SampleVideoEncoder/app/src/main/res/values/styles.xml
@@ -0,0 +1,10 @@
+<resources>
+ <!-- Base application theme. -->
+ <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
+ <!-- Customize your theme here. -->
+ <item name="colorPrimary">@color/colorPrimary</item>
+ <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
+ <item name="colorAccent">@color/colorAccent</item>
+ </style>
+
+</resources>
\ No newline at end of file
diff --git a/media/tests/SampleVideoEncoder/build.gradle b/media/tests/SampleVideoEncoder/build.gradle
new file mode 100644
index 0000000..4ca0c7e
--- /dev/null
+++ b/media/tests/SampleVideoEncoder/build.gradle
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+buildscript {
+ repositories {
+ google()
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:4.1.1'
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ jcenter()
+ }
+}
\ No newline at end of file
diff --git a/media/tests/SampleVideoEncoder/gradle.properties b/media/tests/SampleVideoEncoder/gradle.properties
new file mode 100644
index 0000000..5ae443b
--- /dev/null
+++ b/media/tests/SampleVideoEncoder/gradle.properties
@@ -0,0 +1,4 @@
+# Project-wide Gradle settings.
+org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
+android.useAndroidX=true
+android.enableJetifier=true
diff --git a/media/tests/SampleVideoEncoder/gradle/wrapper/gradle-wrapper.jar b/media/tests/SampleVideoEncoder/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..f6b961f
--- /dev/null
+++ b/media/tests/SampleVideoEncoder/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/media/tests/SampleVideoEncoder/gradle/wrapper/gradle-wrapper.properties b/media/tests/SampleVideoEncoder/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..a9a12eb
--- /dev/null
+++ b/media/tests/SampleVideoEncoder/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Wed Dec 16 10:06:45 IST 2020
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip
diff --git a/media/tests/SampleVideoEncoder/gradlew b/media/tests/SampleVideoEncoder/gradlew
new file mode 100644
index 0000000..cccdd3d
--- /dev/null
+++ b/media/tests/SampleVideoEncoder/gradlew
@@ -0,0 +1,172 @@
+#!/usr/bin/env sh
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/media/tests/SampleVideoEncoder/gradlew.bat b/media/tests/SampleVideoEncoder/gradlew.bat
new file mode 100644
index 0000000..f955316
--- /dev/null
+++ b/media/tests/SampleVideoEncoder/gradlew.bat
@@ -0,0 +1,84 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/media/tests/SampleVideoEncoder/settings.gradle b/media/tests/SampleVideoEncoder/settings.gradle
new file mode 100644
index 0000000..4d3c3a5
--- /dev/null
+++ b/media/tests/SampleVideoEncoder/settings.gradle
@@ -0,0 +1,2 @@
+include ':app'
+rootProject.name = "SampleVideoEncoder"
\ No newline at end of file
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 5a042bd..5b53c0b 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -257,11 +257,7 @@
} else {
checkCloseOutputs();
}
-
- if (mEngine->getPhoneState() == AUDIO_MODE_IN_CALL && hasPrimaryOutput()) {
- DeviceVector newDevices = getNewOutputDevices(mPrimaryOutput, false /*fromCache*/);
- updateCallRouting(newDevices);
- }
+ (void)updateCallRouting(false /*fromCache*/);
const DeviceVector msdOutDevices = getMsdAudioOutDevices();
for (size_t i = 0; i < mOutputs.size(); i++) {
sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(i);
@@ -349,10 +345,7 @@
// getDeviceForStrategy() cache
updateDevicesAndOutputs();
- if (mEngine->getPhoneState() == AUDIO_MODE_IN_CALL && hasPrimaryOutput()) {
- DeviceVector newDevices = getNewOutputDevices(mPrimaryOutput, false /*fromCache*/);
- updateCallRouting(newDevices);
- }
+ (void)updateCallRouting(false /*fromCache*/);
// Reconnect Audio Source
for (const auto &strategy : mEngine->getOrderedProductStrategies()) {
auto attributes = mEngine->getAllAttributesForProductStrategy(strategy).front();
@@ -517,23 +510,58 @@
return status;
}
-uint32_t AudioPolicyManager::updateCallRouting(const DeviceVector &rxDevices, uint32_t delayMs)
+DeviceVector AudioPolicyManager::selectBestRxSinkDevicesForCall(bool fromCache)
+{
+ DeviceVector rxSinkdevices{};
+ rxSinkdevices = mEngine->getOutputDevicesForAttributes(
+ attributes_initializer(AUDIO_USAGE_VOICE_COMMUNICATION), nullptr, fromCache);
+ if (!rxSinkdevices.isEmpty() && mAvailableOutputDevices.contains(rxSinkdevices.itemAt(0))) {
+ auto rxSinkDevice = rxSinkdevices.itemAt(0);
+ auto telephonyRxModule = mHwModules.getModuleForDeviceType(
+ AUDIO_DEVICE_IN_TELEPHONY_RX, AUDIO_FORMAT_DEFAULT);
+ // retrieve Rx Source device descriptor
+ sp<DeviceDescriptor> rxSourceDevice = mAvailableInputDevices.getDevice(
+ AUDIO_DEVICE_IN_TELEPHONY_RX, String8(), AUDIO_FORMAT_DEFAULT);
+
+ // RX Telephony and Rx sink devices are declared by Primary Audio HAL
+ if (isPrimaryModule(telephonyRxModule) && (telephonyRxModule->getHalVersionMajor() >= 3) &&
+ telephonyRxModule->supportsPatch(rxSourceDevice, rxSinkDevice)) {
+ ALOGW("%s() device %s using HW Bridge", __func__, rxSinkDevice->toString().c_str());
+ return DeviceVector(rxSinkDevice);
+ }
+ }
+ // Note that despite the fact that getNewOutputDevices() is called on the primary output,
+ // the device returned is not necessarily reachable via this output
+ // (filter later by setOutputDevices())
+ return getNewOutputDevices(mPrimaryOutput, fromCache);
+}
+
+status_t AudioPolicyManager::updateCallRouting(bool fromCache, uint32_t delayMs, uint32_t *waitMs)
+{
+ if (mEngine->getPhoneState() == AUDIO_MODE_IN_CALL && hasPrimaryOutput()) {
+ DeviceVector rxDevices = selectBestRxSinkDevicesForCall(fromCache);
+ return updateCallRoutingInternal(rxDevices, delayMs, waitMs);
+ }
+ return INVALID_OPERATION;
+}
+
+status_t AudioPolicyManager::updateCallRoutingInternal(
+ const DeviceVector &rxDevices, uint32_t delayMs, uint32_t *waitMs)
{
bool createTxPatch = false;
bool createRxPatch = false;
uint32_t muteWaitMs = 0;
-
if(!hasPrimaryOutput() ||
mPrimaryOutput->devices().onlyContainsDevicesWithType(AUDIO_DEVICE_OUT_STUB)) {
- return muteWaitMs;
+ return INVALID_OPERATION;
}
- ALOG_ASSERT(!rxDevices.isEmpty(), "updateCallRouting() no selected output device");
+ ALOG_ASSERT(!rxDevices.isEmpty(), "%s() no selected output device", __func__);
audio_attributes_t attr = { .source = AUDIO_SOURCE_VOICE_COMMUNICATION };
auto txSourceDevice = mEngine->getInputDeviceForAttributes(attr);
- ALOG_ASSERT(txSourceDevice != 0, "updateCallRouting() input selected device not available");
+ ALOG_ASSERT(txSourceDevice != 0, "%s() input selected device not available", __func__);
- ALOGV("updateCallRouting device rxDevice %s txDevice %s",
+ ALOGV("%s device rxDevice %s txDevice %s", __func__,
rxDevices.itemAt(0)->toString().c_str(), txSourceDevice->toString().c_str());
disconnectTelephonyRxAudioSource();
@@ -562,8 +590,8 @@
(telephonyRxModule->getHalVersionMajor() >= 3)) {
if (rxSourceDevice == 0 || txSinkDevice == 0) {
// RX / TX Telephony device(s) is(are) not currently available
- ALOGE("updateCallRouting() no telephony Tx and/or RX device");
- return muteWaitMs;
+ ALOGE("%s() no telephony Tx and/or RX device", __func__);
+ return INVALID_OPERATION;
}
// createAudioPatchInternal now supports both HW / SW bridging
createRxPatch = true;
@@ -601,8 +629,10 @@
}
mCallTxPatch = createTelephonyPatch(false /*isRx*/, txSourceDevice, delayMs);
}
-
- return muteWaitMs;
+ if (waitMs != nullptr) {
+ *waitMs = muteWaitMs;
+ }
+ return NO_ERROR;
}
sp<AudioPatch> AudioPolicyManager::createTelephonyPatch(
@@ -720,25 +750,22 @@
}
if (hasPrimaryOutput()) {
- // Note that despite the fact that getNewOutputDevices() is called on the primary output,
- // the device returned is not necessarily reachable via this output
- DeviceVector rxDevices = getNewOutputDevices(mPrimaryOutput, false /*fromCache*/);
- // force routing command to audio hardware when ending call
- // even if no device change is needed
- if (isStateInCall(oldState) && rxDevices.isEmpty()) {
- rxDevices = mPrimaryOutput->devices();
- }
-
if (state == AUDIO_MODE_IN_CALL) {
- updateCallRouting(rxDevices, delayMs);
- } else if (oldState == AUDIO_MODE_IN_CALL) {
- disconnectTelephonyRxAudioSource();
- if (mCallTxPatch != 0) {
- releaseAudioPatchInternal(mCallTxPatch->getHandle());
- mCallTxPatch.clear();
- }
- setOutputDevices(mPrimaryOutput, rxDevices, force, 0);
+ (void)updateCallRouting(false /*fromCache*/, delayMs);
} else {
+ DeviceVector rxDevices = getNewOutputDevices(mPrimaryOutput, false /*fromCache*/);
+ // force routing command to audio hardware when ending call
+ // even if no device change is needed
+ if (isStateInCall(oldState) && rxDevices.isEmpty()) {
+ rxDevices = mPrimaryOutput->devices();
+ }
+ if (oldState == AUDIO_MODE_IN_CALL) {
+ disconnectTelephonyRxAudioSource();
+ if (mCallTxPatch != 0) {
+ releaseAudioPatchInternal(mCallTxPatch->getHandle());
+ mCallTxPatch.clear();
+ }
+ }
setOutputDevices(mPrimaryOutput, rxDevices, force, 0);
}
}
@@ -3236,9 +3263,7 @@
void AudioPolicyManager::updateCallAndOutputRouting(bool forceVolumeReeval, uint32_t delayMs)
{
uint32_t waitMs = 0;
- if (mEngine->getPhoneState() == AUDIO_MODE_IN_CALL && hasPrimaryOutput()) {
- DeviceVector newDevices = getNewOutputDevices(mPrimaryOutput, true /*fromCache*/);
- waitMs = updateCallRouting(newDevices, delayMs);
+ if (updateCallRouting(true /*fromCache*/, delayMs, &waitMs) == NO_ERROR) {
// Only apply special touch sound delay once
delayMs = 0;
}
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h
index ed5be5e..5533829 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.h
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h
@@ -729,9 +729,22 @@
String8(devices.itemAt(0)->address().c_str()) : String8("");
}
- uint32_t updateCallRouting(const DeviceVector &rxDevices, uint32_t delayMs = 0);
+ status_t updateCallRouting(
+ bool fromCache, uint32_t delayMs = 0, uint32_t *waitMs = nullptr);
+ status_t updateCallRoutingInternal(
+ const DeviceVector &rxDevices, uint32_t delayMs, uint32_t *waitMs);
sp<AudioPatch> createTelephonyPatch(bool isRx, const sp<DeviceDescriptor> &device,
uint32_t delayMs);
+ /**
+ * @brief selectBestRxSinkDevicesForCall: if the primary module host both Telephony Rx/Tx
+ * devices, and it declares also supporting a HW bridge between the Telephony Rx and the
+ * given sink device for Voice Call audio attributes, select this device in prio.
+ * Otherwise, getNewOutputDevices() is called on the primary output to select sink device.
+ * @param fromCache true to prevent engine reconsidering all product strategies and retrieve
+ * from engine cache.
+ * @return vector of devices, empty if none is found.
+ */
+ DeviceVector selectBestRxSinkDevicesForCall(bool fromCache);
bool isDeviceOfModule(const sp<DeviceDescriptor>& devDesc, const char *moduleId) const;
status_t startSource(const sp<SwAudioOutputDescriptor>& outputDesc,
diff --git a/services/audiopolicy/service/AudioPolicyEffects.cpp b/services/audiopolicy/service/AudioPolicyEffects.cpp
index b738633..5dac55b 100644
--- a/services/audiopolicy/service/AudioPolicyEffects.cpp
+++ b/services/audiopolicy/service/AudioPolicyEffects.cpp
@@ -970,7 +970,7 @@
for (const auto& deviceEffectsIter : mDeviceEffects) {
const auto& deviceEffects = deviceEffectsIter.second;
for (const auto& effectDesc : deviceEffects->mEffectDescriptors->mEffects) {
- auto fx = std::make_unique<AudioEffect>(String16("android"));
+ sp<AudioEffect> fx = new AudioEffect(String16("android"));
fx->set(EFFECT_UUID_NULL, &effectDesc->mUuid, 0, nullptr,
nullptr, AUDIO_SESSION_DEVICE, AUDIO_IO_HANDLE_NONE,
AudioDeviceTypeAddr{deviceEffects->getDeviceType(),
@@ -987,7 +987,7 @@
ALOGV("%s(): create Fx %s added on port type=%d address=%s", __func__,
effectDesc->mName, deviceEffects->getDeviceType(),
deviceEffects->getDeviceAddress().c_str());
- deviceEffects->mEffects.push_back(std::move(fx));
+ deviceEffects->mEffects.push_back(fx);
}
}
}
diff --git a/services/audiopolicy/service/AudioPolicyEffects.h b/services/audiopolicy/service/AudioPolicyEffects.h
index 81c728d..13d5d0c 100644
--- a/services/audiopolicy/service/AudioPolicyEffects.h
+++ b/services/audiopolicy/service/AudioPolicyEffects.h
@@ -207,7 +207,7 @@
mDeviceType(device), mDeviceAddress(address) {}
/*virtual*/ ~DeviceEffects() = default;
- std::vector<std::unique_ptr<AudioEffect>> mEffects;
+ std::vector< sp<AudioEffect> > mEffects;
audio_devices_t getDeviceType() const { return mDeviceType; }
std::string getDeviceAddress() const { return mDeviceAddress; }
const std::unique_ptr<EffectDescVector> mEffectDescriptors;
diff --git a/services/mediametrics/AudioAnalytics.cpp b/services/mediametrics/AudioAnalytics.cpp
index d78d1e3..2a52599 100644
--- a/services/mediametrics/AudioAnalytics.cpp
+++ b/services/mediametrics/AudioAnalytics.cpp
@@ -348,7 +348,7 @@
} else {
ss << "Statsd atoms:\n" << statsd;
}
- ll -= n;
+ ll -= (int32_t)n;
}
}
@@ -737,7 +737,7 @@
mA2dpConnectionServiceNs = 0;
++mA2dpConnectionSuccesses;
- const auto connectionTimeMs = float(timeDiffNs * 1e-6);
+ const auto connectionTimeMs = float((double)timeDiffNs * 1e-6);
const auto outputDeviceBits = types::lookup<types::OUTPUT_DEVICE, long_enum_type_t>(
"AUDIO_DEVICE_OUT_BLUETOOTH_A2DP");
diff --git a/services/mediametrics/AudioPowerUsage.cpp b/services/mediametrics/AudioPowerUsage.cpp
index 34be0b9..e584f12 100644
--- a/services/mediametrics/AudioPowerUsage.cpp
+++ b/services/mediametrics/AudioPowerUsage.cpp
@@ -174,8 +174,8 @@
if (item_device == device && item_type == type) {
int64_t final_duration_ns = item_duration_ns + duration_ns;
double final_volume = (device & INPUT_DEVICE_BIT) ? 1.0:
- ((item_volume * item_duration_ns +
- average_vol * duration_ns) / final_duration_ns);
+ ((item_volume * (double)item_duration_ns +
+ average_vol * (double)duration_ns) / (double)final_duration_ns);
item->setInt64(AUDIO_POWER_USAGE_PROP_DURATION_NS, final_duration_ns);
item->setDouble(AUDIO_POWER_USAGE_PROP_VOLUME, final_volume);
@@ -289,7 +289,7 @@
const int64_t durationNs = endCallNs - mDeviceTimeNs;
if (durationNs > 0) {
mDeviceVolume = (mDeviceVolume * double(mVolumeTimeNs - mDeviceTimeNs) +
- mVoiceVolume * double(endCallNs - mVolumeTimeNs)) / durationNs;
+ mVoiceVolume * double(endCallNs - mVolumeTimeNs)) / (double)durationNs;
saveAsItems_l(mPrimaryDevice, durationNs, VOICE_CALL_TYPE, mDeviceVolume);
}
} else if (mode == "AUDIO_MODE_IN_CALL") { // entering call mode
@@ -317,7 +317,7 @@
const int64_t durationNs = timeNs - mDeviceTimeNs;
if (durationNs > 0) {
mDeviceVolume = (mDeviceVolume * double(mVolumeTimeNs - mDeviceTimeNs) +
- mVoiceVolume * double(timeNs - mVolumeTimeNs)) / durationNs;
+ mVoiceVolume * double(timeNs - mVolumeTimeNs)) / (double)durationNs;
mVolumeTimeNs = timeNs;
}
}
@@ -348,7 +348,7 @@
const int64_t durationNs = endDeviceNs - mDeviceTimeNs;
if (durationNs > 0) {
mDeviceVolume = (mDeviceVolume * double(mVolumeTimeNs - mDeviceTimeNs) +
- mVoiceVolume * double(endDeviceNs - mVolumeTimeNs)) / durationNs;
+ mVoiceVolume * double(endDeviceNs - mVolumeTimeNs)) / (double)durationNs;
saveAsItems_l(mPrimaryDevice, durationNs, VOICE_CALL_TYPE, mDeviceVolume);
}
// reset statistics
diff --git a/services/mediametrics/MediaMetricsService.cpp b/services/mediametrics/MediaMetricsService.cpp
index bf6e428..0e89054 100644
--- a/services/mediametrics/MediaMetricsService.cpp
+++ b/services/mediametrics/MediaMetricsService.cpp
@@ -107,7 +107,7 @@
{
ALOGD("%s", __func__);
// the class destructor clears anyhow, but we enforce clearing items first.
- mItemsDiscarded += mItems.size();
+ mItemsDiscarded += (int64_t)mItems.size();
mItems.clear();
}
@@ -287,7 +287,7 @@
std::lock_guard _l(mLock);
if (clear) {
- mItemsDiscarded += mItems.size();
+ mItemsDiscarded += (int64_t)mItems.size();
mItems.clear();
mAudioAnalytics.clear();
} else {
@@ -416,10 +416,10 @@
if (const size_t toErase = overlimit + expired;
toErase > 0) {
- mItemsDiscardedCount += overlimit;
- mItemsDiscardedExpire += expired;
- mItemsDiscarded += toErase;
- mItems.erase(mItems.begin(), mItems.begin() + toErase); // erase from front
+ mItemsDiscardedCount += (int64_t)overlimit;
+ mItemsDiscardedExpire += (int64_t)expired;
+ mItemsDiscarded += (int64_t)toErase;
+ mItems.erase(mItems.begin(), mItems.begin() + (ptrdiff_t)toErase); // erase from front
}
return more;
}