Merge "support changing audio-hw-sync-id during playback."
diff --git a/apex/Android.bp b/apex/Android.bp
index 4d194bd..dabf4c2 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -64,6 +64,9 @@
// - build artifacts (lib/javalib/bin) against Android 10 SDK
// so that the artifacts can run.
min_sdk_version: "29",
+ // Indicates that pre-installed version of this apex can be compressed.
+ // Whether it actually will be compressed is controlled on per-device basis.
+ compressible: true,
}
apex {
@@ -120,6 +123,9 @@
// - build artifacts (lib/javalib/bin) against Android 10 SDK
// so that the artifacts can run.
min_sdk_version: "29",
+ // Indicates that pre-installed version of this apex can be compressed.
+ // Whether it actually will be compressed is controlled on per-device basis.
+ compressible: true,
}
prebuilt_etc {
diff --git a/drm/mediacas/plugins/clearkey/ClearKeySessionLibrary.h b/drm/mediacas/plugins/clearkey/ClearKeySessionLibrary.h
index a537e63..7c6d86c 100644
--- a/drm/mediacas/plugins/clearkey/ClearKeySessionLibrary.h
+++ b/drm/mediacas/plugins/clearkey/ClearKeySessionLibrary.h
@@ -22,7 +22,6 @@
#include <openssl/aes.h>
#include <utils/KeyedVector.h>
#include <utils/Mutex.h>
-#include <utils/RefBase.h>
namespace android {
struct ABuffer;
@@ -30,7 +29,7 @@
namespace clearkeycas {
class KeyFetcher;
-class ClearKeyCasSession : public RefBase {
+class ClearKeyCasSession {
public:
explicit ClearKeyCasSession(CasPlugin *plugin);
diff --git a/media/codec2/components/aac/C2SoftAacDec.cpp b/media/codec2/components/aac/C2SoftAacDec.cpp
index 3e6b0ff..332696d 100644
--- a/media/codec2/components/aac/C2SoftAacDec.cpp
+++ b/media/codec2/components/aac/C2SoftAacDec.cpp
@@ -55,6 +55,8 @@
namespace android {
constexpr char COMPONENT_NAME[] = "c2.android.aac.decoder";
+constexpr size_t kDefaultOutputPortDelay = 2;
+constexpr size_t kMaxOutputPortDelay = 16;
class C2SoftAacDec::IntfImpl : public SimpleInterface<void>::BaseParams {
public:
@@ -73,7 +75,9 @@
addParameter(
DefineParam(mActualOutputDelay, C2_PARAMKEY_OUTPUT_DELAY)
- .withConstValue(new C2PortActualDelayTuning::output(2u))
+ .withDefault(new C2PortActualDelayTuning::output(kDefaultOutputPortDelay))
+ .withFields({C2F(mActualOutputDelay, value).inRange(0, kMaxOutputPortDelay)})
+ .withSetter(Setter<decltype(*mActualOutputDelay)>::StrictValueWithNoDeps)
.build());
addParameter(
@@ -263,6 +267,7 @@
mAACDecoder(nullptr),
mStreamInfo(nullptr),
mSignalledError(false),
+ mOutputPortDelay(kDefaultOutputPortDelay),
mOutputDelayRingBuffer(nullptr) {
}
@@ -915,6 +920,29 @@
int32_t outputDelay = mStreamInfo->outputDelay * mStreamInfo->numChannels;
+ size_t numSamplesInOutput = mStreamInfo->frameSize * mStreamInfo->numChannels;
+ if (numSamplesInOutput > 0) {
+ size_t actualOutputPortDelay = (outputDelay + numSamplesInOutput - 1) / numSamplesInOutput;
+ if (actualOutputPortDelay > mOutputPortDelay) {
+ mOutputPortDelay = actualOutputPortDelay;
+ ALOGV("New Output port delay %zu ", mOutputPortDelay);
+
+ C2PortActualDelayTuning::output outputPortDelay(mOutputPortDelay);
+ std::vector<std::unique_ptr<C2SettingResult>> failures;
+ c2_status_t err =
+ mIntf->config({&outputPortDelay}, C2_MAY_BLOCK, &failures);
+ if (err == OK) {
+ work->worklets.front()->output.configUpdate.push_back(
+ C2Param::Copy(outputPortDelay));
+ } else {
+ ALOGE("Cannot set output delay");
+ mSignalledError = true;
+ work->workletsProcessed = 1u;
+ work->result = C2_CORRUPTED;
+ return;
+ }
+ }
+ }
mBuffersInfo.push_back(std::move(inInfo));
work->workletsProcessed = 0u;
if (!eos && mOutputDelayCompensated < outputDelay) {
diff --git a/media/codec2/components/aac/C2SoftAacDec.h b/media/codec2/components/aac/C2SoftAacDec.h
index 965c29e..986187c 100644
--- a/media/codec2/components/aac/C2SoftAacDec.h
+++ b/media/codec2/components/aac/C2SoftAacDec.h
@@ -57,6 +57,7 @@
size_t mInputBufferCount;
size_t mOutputBufferCount;
bool mSignalledError;
+ size_t mOutputPortDelay;
struct Info {
uint64_t frameIndex;
size_t bufferSize;
diff --git a/media/codec2/core/include/C2Buffer.h b/media/codec2/core/include/C2Buffer.h
index fe37b05..a5d6fbf 100644
--- a/media/codec2/core/include/C2Buffer.h
+++ b/media/codec2/core/include/C2Buffer.h
@@ -642,7 +642,8 @@
* \retval C2_REFUSED no permission to complete the allocation
* \retval C2_BAD_VALUE capacity or usage are not supported (invalid) (caller error)
* \retval C2_OMITTED this allocator does not support 1D allocations
- * \retval C2_CORRUPTED some unknown, unrecoverable error occured during allocation (unexpected)
+ * \retval C2_CORRUPTED some unknown, unrecoverable error occurred during allocation
+ * (unexpected)
*/
virtual c2_status_t newLinearAllocation(
uint32_t capacity __unused, C2MemoryUsage usage __unused,
@@ -666,7 +667,8 @@
* \retval C2_REFUSED no permission to recreate the allocation
* \retval C2_BAD_VALUE invalid handle (caller error)
* \retval C2_OMITTED this allocator does not support 1D allocations
- * \retval C2_CORRUPTED some unknown, unrecoverable error occured during allocation (unexpected)
+ * \retval C2_CORRUPTED some unknown, unrecoverable error occurred during allocation
+ * (unexpected)
*/
virtual c2_status_t priorLinearAllocation(
const C2Handle *handle __unused,
@@ -699,7 +701,8 @@
* \retval C2_REFUSED no permission to complete the allocation
* \retval C2_BAD_VALUE width, height, format or usage are not supported (invalid) (caller error)
* \retval C2_OMITTED this allocator does not support 2D allocations
- * \retval C2_CORRUPTED some unknown, unrecoverable error occured during allocation (unexpected)
+ * \retval C2_CORRUPTED some unknown, unrecoverable error occurred during allocation
+ * (unexpected)
*/
virtual c2_status_t newGraphicAllocation(
uint32_t width __unused, uint32_t height __unused, uint32_t format __unused,
@@ -724,7 +727,8 @@
* \retval C2_REFUSED no permission to recreate the allocation
* \retval C2_BAD_VALUE invalid handle (caller error)
* \retval C2_OMITTED this allocator does not support 2D allocations
- * \retval C2_CORRUPTED some unknown, unrecoverable error occured during recreation (unexpected)
+ * \retval C2_CORRUPTED some unknown, unrecoverable error occurred during recreation
+ * (unexpected)
*/
virtual c2_status_t priorGraphicAllocation(
const C2Handle *handle __unused,
@@ -908,7 +912,8 @@
* \retval C2_REFUSED no permission to complete any required allocation
* \retval C2_BAD_VALUE capacity or usage are not supported (invalid) (caller error)
* \retval C2_OMITTED this pool does not support linear blocks
- * \retval C2_CORRUPTED some unknown, unrecoverable error occured during operation (unexpected)
+ * \retval C2_CORRUPTED some unknown, unrecoverable error occurred during operation
+ * (unexpected)
*/
virtual c2_status_t fetchLinearBlock(
uint32_t capacity __unused, C2MemoryUsage usage __unused,
@@ -937,7 +942,8 @@
* \retval C2_REFUSED no permission to complete any required allocation
* \retval C2_BAD_VALUE capacity or usage are not supported (invalid) (caller error)
* \retval C2_OMITTED this pool does not support circular blocks
- * \retval C2_CORRUPTED some unknown, unrecoverable error occured during operation (unexpected)
+ * \retval C2_CORRUPTED some unknown, unrecoverable error occurred during operation
+ * (unexpected)
*/
virtual c2_status_t fetchCircularBlock(
uint32_t capacity __unused, C2MemoryUsage usage __unused,
@@ -969,7 +975,8 @@
* \retval C2_BAD_VALUE width, height, format or usage are not supported (invalid) (caller
* error)
* \retval C2_OMITTED this pool does not support 2D blocks
- * \retval C2_CORRUPTED some unknown, unrecoverable error occured during operation (unexpected)
+ * \retval C2_CORRUPTED some unknown, unrecoverable error occurred during operation
+ * (unexpected)
*/
virtual c2_status_t fetchGraphicBlock(
uint32_t width __unused, uint32_t height __unused, uint32_t format __unused,
@@ -980,6 +987,90 @@
}
virtual ~C2BlockPool() = default;
+
+ /**
+ * Blocking fetch for linear block. Obtains a linear writable block of given |capacity|
+ * and |usage|. If a block can be successfully obtained, the block is stored in |block|,
+ * |fence| is set to a null-fence and C2_OK is returned.
+ *
+ * If a block cannot be temporarily obtained, |block| is set to nullptr, a waitable fence
+ * is stored into |fence| and C2_BLOCKING is returned.
+ *
+ * Otherwise, |block| is set to nullptr and |fence| is set to a null-fence. The waitable
+ * fence is signalled when the temporary restriction on fetch is lifted.
+ * e.g. more memory is available to fetch because some meomory or prior blocks were released.
+ *
+ * \param capacity the size of requested block.
+ * \param usage the memory usage info for the requested block. Returned blocks will be
+ * optimized for this usage, but may be used with any usage. One exception:
+ * protected blocks/buffers can only be used in a protected scenario.
+ * \param block pointer to where the obtained block shall be stored on success. nullptr will
+ * be stored here on failure
+ * \param fence pointer to where the fence shall be stored on C2_BLOCKING error.
+ *
+ * \retval C2_OK the operation was successful
+ * \retval C2_NO_MEMORY not enough memory to complete any required allocation
+ * \retval C2_TIMED_OUT the operation timed out
+ * \retval C2_BLOCKING the operation is blocked
+ * \retval C2_REFUSED no permission to complete any required allocation
+ * \retval C2_BAD_VALUE capacity or usage are not supported (invalid) (caller error)
+ * \retval C2_OMITTED this pool does not support linear blocks nor fence.
+ * \retval C2_CORRUPTED some unknown, unrecoverable error occurred during operation
+ * (unexpected)
+ */
+ virtual c2_status_t fetchLinearBlock(
+ uint32_t capacity __unused, C2MemoryUsage usage __unused,
+ std::shared_ptr<C2LinearBlock> *block /* nonnull */,
+ C2Fence *fence /* nonnull */) {
+ *block = nullptr;
+ (void) fence;
+ return C2_OMITTED;
+ }
+
+ /**
+ * Blocking fetch for 2D graphic block. Obtains a 2D graphic writable block of given |capacity|
+ * and |usage|. If a block can be successfully obtained, the block is stored in |block|,
+ * |fence| is set to a null-fence and C2_OK is returned.
+ *
+ * If a block cannot be temporarily obtained, |block| is set to nullptr, a waitable fence
+ * is stored into |fence| and C2_BLOCKING is returned.
+ *
+ * Otherwise, |block| is set to nullptr and |fence| is set to a null-fence. The waitable
+ * fence is signalled when the temporary restriction on fetch is lifted.
+ * e.g. more memory is available to fetch because some meomory or prior blocks were released.
+ *
+ * \param width the width of requested block (the obtained block could be slightly larger, e.g.
+ * to accommodate any system-required alignment)
+ * \param height the height of requested block (the obtained block could be slightly larger,
+ * e.g. to accommodate any system-required alignment)
+ * \param format the pixel format of requested block. This could be a vendor specific format.
+ * \param usage the memory usage info for the requested block. Returned blocks will be
+ * optimized for this usage, but may be used with any usage. One exception:
+ * protected blocks/buffers can only be used in a protected scenario.
+ * \param block pointer to where the obtained block shall be stored on success. nullptr
+ * will be stored here on failure
+ * \param fence pointer to where the fence shall be stored on C2_BLOCKING error.
+ *
+ * \retval C2_OK the operation was successful
+ * \retval C2_NO_MEMORY not enough memory to complete any required allocation
+ * \retval C2_TIMED_OUT the operation timed out
+ * \retval C2_BLOCKING the operation is blocked
+ * \retval C2_REFUSED no permission to complete any required allocation
+ * \retval C2_BAD_VALUE width, height, format or usage are not supported (invalid) (caller
+ * error)
+ * \retval C2_OMITTED this pool does not support 2D blocks
+ * \retval C2_CORRUPTED some unknown, unrecoverable error occurred during operation
+ * (unexpected)
+ */
+ virtual c2_status_t fetchGraphicBlock(
+ uint32_t width __unused, uint32_t height __unused, uint32_t format __unused,
+ C2MemoryUsage usage __unused,
+ std::shared_ptr<C2GraphicBlock> *block /* nonnull */,
+ C2Fence *fence /* nonnull */) {
+ *block = nullptr;
+ (void) fence;
+ return C2_OMITTED;
+ }
protected:
C2BlockPool() = default;
};
diff --git a/media/codec2/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioDecTest.cpp b/media/codec2/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioDecTest.cpp
index 3a47ae9..1f95eaf 100644
--- a/media/codec2/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioDecTest.cpp
+++ b/media/codec2/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioDecTest.cpp
@@ -39,8 +39,44 @@
static std::vector<std::tuple<std::string, std::string, std::string>> kCsdFlushTestParameters;
-// Resource directory
-static std::string sResourceDir = "";
+struct CompToURL {
+ std::string mime;
+ std::string mURL;
+ std::string info;
+};
+
+std::vector<CompToURL> kCompToURL = {
+ {"mp4a-latm",
+ "bbb_aac_stereo_128kbps_48000hz.aac", "bbb_aac_stereo_128kbps_48000hz.info"},
+ {"mp4a-latm",
+ "bbb_aac_stereo_128kbps_48000hz.aac", "bbb_aac_stereo_128kbps_48000hz_multi_frame.info"},
+ {"audio/mpeg",
+ "bbb_mp3_stereo_192kbps_48000hz.mp3", "bbb_mp3_stereo_192kbps_48000hz.info"},
+ {"audio/mpeg",
+ "bbb_mp3_stereo_192kbps_48000hz.mp3", "bbb_mp3_stereo_192kbps_48000hz_multi_frame.info"},
+ {"3gpp",
+ "sine_amrnb_1ch_12kbps_8000hz.amrnb", "sine_amrnb_1ch_12kbps_8000hz.info"},
+ {"3gpp",
+ "sine_amrnb_1ch_12kbps_8000hz.amrnb", "sine_amrnb_1ch_12kbps_8000hz_multi_frame.info"},
+ {"amr-wb",
+ "bbb_amrwb_1ch_14kbps_16000hz.amrwb", "bbb_amrwb_1ch_14kbps_16000hz.info"},
+ {"amr-wb",
+ "bbb_amrwb_1ch_14kbps_16000hz.amrwb", "bbb_amrwb_1ch_14kbps_16000hz_multi_frame.info"},
+ {"vorbis",
+ "bbb_vorbis_stereo_128kbps_48000hz.vorbis", "bbb_vorbis_stereo_128kbps_48000hz.info"},
+ {"opus",
+ "bbb_opus_stereo_128kbps_48000hz.opus", "bbb_opus_stereo_128kbps_48000hz.info"},
+ {"g711-alaw",
+ "bbb_g711alaw_1ch_8khz.raw", "bbb_g711alaw_1ch_8khz.info"},
+ {"g711-mlaw",
+ "bbb_g711mulaw_1ch_8khz.raw", "bbb_g711mulaw_1ch_8khz.info"},
+ {"gsm",
+ "bbb_gsm_1ch_8khz_13kbps.raw", "bbb_gsm_1ch_8khz_13kbps.info"},
+ {"raw",
+ "bbb_raw_1ch_8khz_s32le.raw", "bbb_raw_1ch_8khz_s32le.info"},
+ {"flac",
+ "bbb_flac_stereo_680kbps_48000hz.flac", "bbb_flac_stereo_680kbps_48000hz.info"},
+};
class LinearBuffer : public C2Buffer {
public:
@@ -76,33 +112,17 @@
mLinearPool = std::make_shared<C2PooledBlockPool>(mLinearAllocator, mBlockPoolId++);
ASSERT_NE(mLinearPool, nullptr);
- mCompName = unknown_comp;
- struct StringToName {
- const char* Name;
- standardComp CompName;
- };
- const StringToName kStringToName[] = {
- {"xaac", xaac}, {"mp3", mp3}, {"amrnb", amrnb},
- {"amrwb", amrwb}, {"aac", aac}, {"vorbis", vorbis},
- {"opus", opus}, {"pcm", pcm}, {"g711.alaw", g711alaw},
- {"g711.mlaw", g711mlaw}, {"gsm", gsm}, {"raw", raw},
- {"flac", flac},
- };
- const size_t kNumStringToName = sizeof(kStringToName) / sizeof(kStringToName[0]);
+ std::vector<std::unique_ptr<C2Param>> queried;
+ mComponent->query({}, {C2PortMediaTypeSetting::input::PARAM_TYPE}, C2_DONT_BLOCK, &queried);
+ ASSERT_GT(queried.size(), 0);
- // Find the component type
- for (size_t i = 0; i < kNumStringToName; ++i) {
- if (strcasestr(mComponentName.c_str(), kStringToName[i].Name)) {
- mCompName = kStringToName[i].CompName;
- break;
- }
- }
+ mMime = ((C2PortMediaTypeSetting::input*)queried[0].get())->m.value;
+
mEos = false;
mFramesReceived = 0;
mTimestampUs = 0u;
mWorkResult = C2_OK;
mTimestampDevTest = false;
- if (mCompName == unknown_comp) mDisableTest = true;
if (mDisableTest) std::cout << "[ WARN ] Test Disabled \n";
}
@@ -119,6 +139,8 @@
virtual void validateTimestampList(int32_t* bitStreamInfo);
+ void GetURLForComponent(char* mURL, char* info, size_t streamIndex = 0);
+
struct outputMetaData {
uint64_t timestampUs;
uint32_t rangeLength;
@@ -158,29 +180,12 @@
}
}
- enum standardComp {
- xaac,
- mp3,
- amrnb,
- amrwb,
- aac,
- vorbis,
- opus,
- pcm,
- g711alaw,
- g711mlaw,
- gsm,
- raw,
- flac,
- unknown_comp,
- };
-
+ std::string mMime;
std::string mInstanceName;
std::string mComponentName;
bool mEos;
bool mDisableTest;
bool mTimestampDevTest;
- standardComp mCompName;
int32_t mWorkResult;
uint64_t mTimestampUs;
@@ -217,7 +222,7 @@
};
void validateComponent(const std::shared_ptr<android::Codec2Client::Component>& component,
- Codec2AudioDecHidlTest::standardComp compName, bool& disableTest) {
+ bool& disableTest) {
// Validate its a C2 Component
if (component->getName().find("c2") == std::string::npos) {
ALOGE("Not a c2 component");
@@ -244,13 +249,6 @@
return;
}
}
-
- // Validates component name
- if (compName == Codec2AudioDecHidlTest::unknown_comp) {
- ALOGE("Component InValid");
- disableTest = true;
- return;
- }
ALOGV("Component Valid");
}
@@ -271,7 +269,7 @@
// parsing the header of elementary stream. Client needs to collect this
// information and reconfigure
void getInputChannelInfo(const std::shared_ptr<android::Codec2Client::Component>& component,
- Codec2AudioDecHidlTest::standardComp compName, int32_t* bitStreamInfo) {
+ std::string mime, int32_t* bitStreamInfo) {
// query nSampleRate and nChannels
std::initializer_list<C2Param::Index> indices{
C2StreamSampleRateInfo::output::PARAM_TYPE,
@@ -288,89 +286,29 @@
C2Param* param = inParams[i].get();
bitStreamInfo[i] = *(int32_t*)((uint8_t*)param + offset);
}
- switch (compName) {
- case Codec2AudioDecHidlTest::amrnb: {
- ASSERT_EQ(bitStreamInfo[0], 8000);
- ASSERT_EQ(bitStreamInfo[1], 1);
- break;
- }
- case Codec2AudioDecHidlTest::amrwb: {
- ASSERT_EQ(bitStreamInfo[0], 16000);
- ASSERT_EQ(bitStreamInfo[1], 1);
- break;
- }
- case Codec2AudioDecHidlTest::gsm: {
- ASSERT_EQ(bitStreamInfo[0], 8000);
- break;
- }
- default:
- break;
+ if (mime.find("3gpp") != std::string::npos) {
+ ASSERT_EQ(bitStreamInfo[0], 8000);
+ ASSERT_EQ(bitStreamInfo[1], 1);
+ } else if (mime.find("amr-wb") != std::string::npos) {
+ ASSERT_EQ(bitStreamInfo[0], 16000);
+ ASSERT_EQ(bitStreamInfo[1], 1);
+ } else if (mime.find("gsm") != std::string::npos) {
+ ASSERT_EQ(bitStreamInfo[0], 8000);
}
}
}
-// number of elementary streams per component
-#define STREAM_COUNT 2
-
// LookUpTable of clips and metadata for component testing
-void GetURLForComponent(Codec2AudioDecHidlTest::standardComp comp, char* mURL, char* info,
- size_t streamIndex = 0) {
- struct CompToURL {
- Codec2AudioDecHidlTest::standardComp comp;
- const char mURL[STREAM_COUNT][512];
- const char info[STREAM_COUNT][512];
- };
- ASSERT_TRUE(streamIndex < STREAM_COUNT);
-
- static const CompToURL kCompToURL[] = {
- {Codec2AudioDecHidlTest::standardComp::xaac,
- {"bbb_aac_stereo_128kbps_48000hz.aac", "bbb_aac_stereo_128kbps_48000hz.aac"},
- {"bbb_aac_stereo_128kbps_48000hz.info",
- "bbb_aac_stereo_128kbps_48000hz_multi_frame.info"}},
- {Codec2AudioDecHidlTest::standardComp::mp3,
- {"bbb_mp3_stereo_192kbps_48000hz.mp3", "bbb_mp3_stereo_192kbps_48000hz.mp3"},
- {"bbb_mp3_stereo_192kbps_48000hz.info",
- "bbb_mp3_stereo_192kbps_48000hz_multi_frame.info"}},
- {Codec2AudioDecHidlTest::standardComp::aac,
- {"bbb_aac_stereo_128kbps_48000hz.aac", "bbb_aac_stereo_128kbps_48000hz.aac"},
- {"bbb_aac_stereo_128kbps_48000hz.info",
- "bbb_aac_stereo_128kbps_48000hz_multi_frame.info"}},
- {Codec2AudioDecHidlTest::standardComp::amrnb,
- {"sine_amrnb_1ch_12kbps_8000hz.amrnb", "sine_amrnb_1ch_12kbps_8000hz.amrnb"},
- {"sine_amrnb_1ch_12kbps_8000hz.info",
- "sine_amrnb_1ch_12kbps_8000hz_multi_frame.info"}},
- {Codec2AudioDecHidlTest::standardComp::amrwb,
- {"bbb_amrwb_1ch_14kbps_16000hz.amrwb", "bbb_amrwb_1ch_14kbps_16000hz.amrwb"},
- {"bbb_amrwb_1ch_14kbps_16000hz.info",
- "bbb_amrwb_1ch_14kbps_16000hz_multi_frame.info"}},
- {Codec2AudioDecHidlTest::standardComp::vorbis,
- {"bbb_vorbis_stereo_128kbps_48000hz.vorbis", ""},
- {"bbb_vorbis_stereo_128kbps_48000hz.info", ""}},
- {Codec2AudioDecHidlTest::standardComp::opus,
- {"bbb_opus_stereo_128kbps_48000hz.opus", ""},
- {"bbb_opus_stereo_128kbps_48000hz.info", ""}},
- {Codec2AudioDecHidlTest::standardComp::g711alaw,
- {"bbb_g711alaw_1ch_8khz.raw", ""},
- {"bbb_g711alaw_1ch_8khz.info", ""}},
- {Codec2AudioDecHidlTest::standardComp::g711mlaw,
- {"bbb_g711mulaw_1ch_8khz.raw", ""},
- {"bbb_g711mulaw_1ch_8khz.info", ""}},
- {Codec2AudioDecHidlTest::standardComp::gsm,
- {"bbb_gsm_1ch_8khz_13kbps.raw", ""},
- {"bbb_gsm_1ch_8khz_13kbps.info", ""}},
- {Codec2AudioDecHidlTest::standardComp::raw,
- {"bbb_raw_1ch_8khz_s32le.raw", ""},
- {"bbb_raw_1ch_8khz_s32le.info", ""}},
- {Codec2AudioDecHidlTest::standardComp::flac,
- {"bbb_flac_stereo_680kbps_48000hz.flac", ""},
- {"bbb_flac_stereo_680kbps_48000hz.info", ""}},
- };
-
- for (size_t i = 0; i < sizeof(kCompToURL) / sizeof(kCompToURL[0]); ++i) {
- if (kCompToURL[i].comp == comp) {
- strcat(mURL, kCompToURL[i].mURL[streamIndex]);
- strcat(info, kCompToURL[i].info[streamIndex]);
- return;
+void Codec2AudioDecHidlTestBase::GetURLForComponent(char* mURL, char* info, size_t streamIndex) {
+ int streamCount = 0;
+ for (size_t i = 0; i < kCompToURL.size(); ++i) {
+ if (mMime.find(kCompToURL[i].mime) != std::string::npos) {
+ if (streamCount == streamIndex) {
+ strcat(mURL, kCompToURL[i].mURL.c_str());
+ strcat(info, kCompToURL[i].info.c_str());
+ return;
+ }
+ streamCount++;
}
}
}
@@ -461,7 +399,7 @@
void Codec2AudioDecHidlTestBase::validateTimestampList(int32_t* bitStreamInfo) {
uint32_t samplesReceived = 0;
// Update SampleRate and ChannelCount
- ASSERT_NO_FATAL_FAILURE(getInputChannelInfo(mComponent, mCompName, bitStreamInfo));
+ ASSERT_NO_FATAL_FAILURE(getInputChannelInfo(mComponent, mMime, bitStreamInfo));
int32_t nSampleRate = bitStreamInfo[0];
int32_t nChannels = bitStreamInfo[1];
std::list<uint64_t>::iterator itIn = mTimestampUslist.begin();
@@ -486,7 +424,7 @@
TEST_P(Codec2AudioDecHidlTest, validateCompName) {
if (mDisableTest) GTEST_SKIP() << "Test is disabled";
ALOGV("Checks if the given component is a valid audio component");
- validateComponent(mComponent, mCompName, mDisableTest);
+ validateComponent(mComponent, mDisableTest);
ASSERT_EQ(mDisableTest, false);
}
@@ -495,7 +433,7 @@
if (mDisableTest) GTEST_SKIP() << "Test is disabled";
ASSERT_EQ(mComponent->start(), C2_OK);
int32_t bitStreamInfo[2] = {0};
- ASSERT_NO_FATAL_FAILURE(getInputChannelInfo(mComponent, mCompName, bitStreamInfo));
+ ASSERT_NO_FATAL_FAILURE(getInputChannelInfo(mComponent, mMime, bitStreamInfo));
setupConfigParam(mComponent, bitStreamInfo);
ASSERT_EQ(mComponent->stop(), C2_OK);
}
@@ -523,7 +461,7 @@
strcpy(mURL, sResourceDir.c_str());
strcpy(info, sResourceDir.c_str());
- GetURLForComponent(mCompName, mURL, info, streamIndex);
+ GetURLForComponent(mURL, info, streamIndex);
if (!strcmp(mURL, sResourceDir.c_str())) {
ALOGV("EMPTY INPUT sResourceDir.c_str() %s mURL %s ", sResourceDir.c_str(), mURL);
return;
@@ -536,11 +474,11 @@
mFramesReceived = 0;
mTimestampUs = 0;
int32_t bitStreamInfo[2] = {0};
- if (mCompName == raw) {
+ if (mMime.find("raw") != std::string::npos) {
bitStreamInfo[0] = 8000;
bitStreamInfo[1] = 1;
} else {
- ASSERT_NO_FATAL_FAILURE(getInputChannelInfo(mComponent, mCompName, bitStreamInfo));
+ ASSERT_NO_FATAL_FAILURE(getInputChannelInfo(mComponent, mMime, bitStreamInfo));
}
if (!setupConfigParam(mComponent, bitStreamInfo)) {
std::cout << "[ WARN ] Test Skipped \n";
@@ -591,17 +529,17 @@
strcpy(mURL, sResourceDir.c_str());
strcpy(info, sResourceDir.c_str());
- GetURLForComponent(mCompName, mURL, info);
+ GetURLForComponent(mURL, info);
int32_t numCsds = populateInfoVector(info, &Info, mTimestampDevTest, &mTimestampUslist);
ASSERT_GE(numCsds, 0) << "Error in parsing input info file: " << info;
int32_t bitStreamInfo[2] = {0};
- if (mCompName == raw) {
+ if (mMime.find("raw") != std::string::npos) {
bitStreamInfo[0] = 8000;
bitStreamInfo[1] = 1;
} else {
- ASSERT_NO_FATAL_FAILURE(getInputChannelInfo(mComponent, mCompName, bitStreamInfo));
+ ASSERT_NO_FATAL_FAILURE(getInputChannelInfo(mComponent, mMime, bitStreamInfo));
}
if (!setupConfigParam(mComponent, bitStreamInfo)) {
std::cout << "[ WARN ] Test Skipped \n";
@@ -683,17 +621,17 @@
strcpy(mURL, sResourceDir.c_str());
strcpy(info, sResourceDir.c_str());
- GetURLForComponent(mCompName, mURL, info);
+ GetURLForComponent(mURL, info);
int32_t numCsds = populateInfoVector(info, &Info, mTimestampDevTest, &mTimestampUslist);
ASSERT_GE(numCsds, 0) << "Error in parsing input info file: " << info;
int32_t bitStreamInfo[2] = {0};
- if (mCompName == raw) {
+ if (mMime.find("raw") != std::string::npos) {
bitStreamInfo[0] = 8000;
bitStreamInfo[1] = 1;
} else {
- ASSERT_NO_FATAL_FAILURE(getInputChannelInfo(mComponent, mCompName, bitStreamInfo));
+ ASSERT_NO_FATAL_FAILURE(getInputChannelInfo(mComponent, mMime, bitStreamInfo));
}
if (!setupConfigParam(mComponent, bitStreamInfo)) {
std::cout << "[ WARN ] Test Skipped \n";
@@ -768,7 +706,7 @@
strcpy(mURL, sResourceDir.c_str());
strcpy(info, sResourceDir.c_str());
- GetURLForComponent(mCompName, mURL, info);
+ GetURLForComponent(mURL, info);
eleInfo.open(info);
ASSERT_EQ(eleInfo.is_open(), true) << mURL << " - file not found";
@@ -798,11 +736,11 @@
}
eleInfo.close();
int32_t bitStreamInfo[2] = {0};
- if (mCompName == raw) {
+ if (mMime.find("raw") != std::string::npos) {
bitStreamInfo[0] = 8000;
bitStreamInfo[1] = 1;
} else {
- ASSERT_NO_FATAL_FAILURE(getInputChannelInfo(mComponent, mCompName, bitStreamInfo));
+ ASSERT_NO_FATAL_FAILURE(getInputChannelInfo(mComponent, mMime, bitStreamInfo));
}
if (!setupConfigParam(mComponent, bitStreamInfo)) {
std::cout << "[ WARN ] Test Skipped \n";
@@ -853,7 +791,7 @@
strcpy(mURL, sResourceDir.c_str());
strcpy(info, sResourceDir.c_str());
- GetURLForComponent(mCompName, mURL, info);
+ GetURLForComponent(mURL, info);
if (!strcmp(mURL, sResourceDir.c_str())) {
ALOGV("EMPTY INPUT sResourceDir.c_str() %s mURL %s ", sResourceDir.c_str(), mURL);
return;
@@ -864,11 +802,11 @@
ASSERT_GE(numCsds, 0) << "Error in parsing input info file";
int32_t bitStreamInfo[2] = {0};
- if (mCompName == raw) {
+ if (mMime.find("raw") != std::string::npos) {
bitStreamInfo[0] = 8000;
bitStreamInfo[1] = 1;
} else {
- ASSERT_NO_FATAL_FAILURE(getInputChannelInfo(mComponent, mCompName, bitStreamInfo));
+ ASSERT_NO_FATAL_FAILURE(getInputChannelInfo(mComponent, mMime, bitStreamInfo));
}
if (!setupConfigParam(mComponent, bitStreamInfo)) {
std::cout << "[ WARN ] Test Skipped \n";
@@ -951,6 +889,7 @@
} // anonymous namespace
int main(int argc, char** argv) {
+ parseArgs(argc, argv);
kTestParameters = getTestParameters(C2Component::DOMAIN_AUDIO, C2Component::KIND_DECODER);
for (auto params : kTestParameters) {
kDecodeTestParameters.push_back(
@@ -968,15 +907,6 @@
std::make_tuple(std::get<0>(params), std::get<1>(params), "false"));
}
- // Set the resource directory based on command line args.
- // Test will fail to set up if the argument is not set.
- for (int i = 1; i < argc; i++) {
- if (strcmp(argv[i], "-P") == 0 && i < argc - 1) {
- sResourceDir = argv[i + 1];
- break;
- }
- }
-
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
diff --git a/media/codec2/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioEncTest.cpp b/media/codec2/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioEncTest.cpp
index e3a4f68..1445e59 100644
--- a/media/codec2/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioEncTest.cpp
+++ b/media/codec2/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioEncTest.cpp
@@ -38,9 +38,6 @@
static std::vector<std::tuple<std::string, std::string, std::string, std::string>>
kEncodeTestParameters;
-// Resource directory
-static std::string sResourceDir = "";
-
class LinearBuffer : public C2Buffer {
public:
explicit LinearBuffer(const std::shared_ptr<C2LinearBlock>& block)
@@ -797,6 +794,7 @@
} // anonymous namespace
int main(int argc, char** argv) {
+ parseArgs(argc, argv);
kTestParameters = getTestParameters(C2Component::DOMAIN_AUDIO, C2Component::KIND_ENCODER);
for (auto params : kTestParameters) {
kEncodeTestParameters.push_back(
@@ -809,15 +807,6 @@
std::make_tuple(std::get<0>(params), std::get<1>(params), "true", "2"));
}
- // Set the resource directory based on command line args.
- // Test will fail to set up if the argument is not set.
- for (int i = 1; i < argc; i++) {
- if (strcmp(argv[i], "-P") == 0 && i < argc - 1) {
- sResourceDir = argv[i + 1];
- break;
- }
- }
-
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
diff --git a/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.cpp b/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.cpp
index 0251ec2..de34705 100644
--- a/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.cpp
+++ b/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.cpp
@@ -22,6 +22,48 @@
#include <android/hardware/media/c2/1.0/IComponentStore.h>
+std::string sResourceDir = "";
+
+std::string sComponentNamePrefix = "";
+
+static constexpr struct option kArgOptions[] = {
+ {"res", required_argument, 0, 'P'},
+ {"prefix", required_argument, 0, 'p'},
+ {"help", required_argument, 0, 'h'},
+ {nullptr, 0, nullptr, 0},
+};
+
+void printUsage(char *me) {
+ std::cerr << "VTS tests to test codec2 components \n";
+ std::cerr << "Usage: " << me << " [options] \n";
+ std::cerr << "\t -P, --res: Mandatory path to a folder that contains test resources \n";
+ std::cerr << "\t -p, --prefix: Optional prefix to select component/s to be tested \n";
+ std::cerr << "\t All codecs are tested by default \n";
+ std::cerr << "\t Eg: c2.android - test codecs starting with c2.android \n";
+ std::cerr << "\t Eg: c2.android.aac.decoder - test a specific codec \n";
+ std::cerr << "\t -h, --help: Print usage \n";
+}
+
+void parseArgs(int argc, char** argv) {
+ int arg;
+ int option_index;
+ while ((arg = getopt_long(argc, argv, ":P:p:h", kArgOptions, &option_index)) != -1) {
+ switch (arg) {
+ case 'P':
+ sResourceDir = optarg;
+ break;
+ case 'p':
+ sComponentNamePrefix = optarg;
+ break;
+ case 'h':
+ printUsage(argv[0]);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
// Test the codecs for NullBuffer, Empty Input Buffer with(out) flags set
void testInputBuffer(const std::shared_ptr<android::Codec2Client::Component>& component,
std::mutex& queueLock, std::list<std::unique_ptr<C2Work>>& workQueue,
@@ -157,11 +199,18 @@
(traits.domain != domain || traits.kind != kind)) {
continue;
}
-
+ if (traits.name.rfind(sComponentNamePrefix, 0) != 0) {
+ ALOGD("Skipping tests for %s. Prefix specified is %s", traits.name.c_str(),
+ sComponentNamePrefix.c_str());
+ continue;
+ }
parameters.push_back(std::make_tuple(instance, traits.name));
}
}
+ if (parameters.empty()) {
+ ALOGE("No test parameters added. Verify component prefix passed to the test");
+ }
return parameters;
}
diff --git a/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.h b/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.h
index 50e3ac5..a2f1561 100644
--- a/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.h
+++ b/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.h
@@ -42,6 +42,12 @@
static std::vector<std::tuple<std::string, std::string>> kTestParameters;
+// Resource directory
+extern std::string sResourceDir;
+
+// Component name prefix
+extern std::string sComponentNamePrefix;
+
struct FrameInfo {
int bytesCount;
uint32_t flags;
@@ -105,6 +111,8 @@
std::function<void(std::list<std::unique_ptr<C2Work>>& workItems)> callBack;
};
+void parseArgs(int argc, char** argv);
+
// Return all test parameters, a list of tuple of <instance, component>.
const std::vector<std::tuple<std::string, std::string>>& getTestParameters();
diff --git a/media/codec2/hidl/1.0/vts/functional/component/VtsHalMediaC2V1_0TargetComponentTest.cpp b/media/codec2/hidl/1.0/vts/functional/component/VtsHalMediaC2V1_0TargetComponentTest.cpp
index 6122225..0648dd9 100644
--- a/media/codec2/hidl/1.0/vts/functional/component/VtsHalMediaC2V1_0TargetComponentTest.cpp
+++ b/media/codec2/hidl/1.0/vts/functional/component/VtsHalMediaC2V1_0TargetComponentTest.cpp
@@ -360,6 +360,7 @@
// TODO: Add test for Invalid work,
// TODO: Add test for Invalid states
int main(int argc, char** argv) {
+ parseArgs(argc, argv);
kTestParameters = getTestParameters();
for (auto params : kTestParameters) {
kInputTestParameters.push_back(
diff --git a/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.cpp b/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.cpp
index b520c17..f29da0e 100644
--- a/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.cpp
+++ b/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.cpp
@@ -45,8 +45,51 @@
static std::vector<std::tuple<std::string, std::string, std::string>> kCsdFlushTestParameters;
-// Resource directory
-static std::string sResourceDir = "";
+struct CompToURL {
+ std::string mime;
+ std::string mURL;
+ std::string info;
+ std::string chksum;
+};
+std::vector<CompToURL> kCompToURL = {
+ {"avc",
+ "bbb_avc_176x144_300kbps_60fps.h264", "bbb_avc_176x144_300kbps_60fps.info",
+ "bbb_avc_176x144_300kbps_60fps_chksum.md5"},
+ {"avc",
+ "bbb_avc_640x360_768kbps_30fps.h264", "bbb_avc_640x360_768kbps_30fps.info",
+ "bbb_avc_640x360_768kbps_30fps_chksum.md5"},
+ {"hevc",
+ "bbb_hevc_176x144_176kbps_60fps.hevc", "bbb_hevc_176x144_176kbps_60fps.info",
+ "bbb_hevc_176x144_176kbps_60fps_chksum.md5"},
+ {"hevc",
+ "bbb_hevc_640x360_1600kbps_30fps.hevc", "bbb_hevc_640x360_1600kbps_30fps.info",
+ "bbb_hevc_640x360_1600kbps_30fps_chksum.md5"},
+ {"mpeg2",
+ "bbb_mpeg2_176x144_105kbps_25fps.m2v", "bbb_mpeg2_176x144_105kbps_25fps.info", ""},
+ {"mpeg2",
+ "bbb_mpeg2_352x288_1mbps_60fps.m2v","bbb_mpeg2_352x288_1mbps_60fps.info", ""},
+ {"3gpp",
+ "bbb_h263_352x288_300kbps_12fps.h263", "bbb_h263_352x288_300kbps_12fps.info", ""},
+ {"mp4v-es",
+ "bbb_mpeg4_352x288_512kbps_30fps.m4v", "bbb_mpeg4_352x288_512kbps_30fps.info", ""},
+ {"vp8",
+ "bbb_vp8_176x144_240kbps_60fps.vp8", "bbb_vp8_176x144_240kbps_60fps.info", ""},
+ {"vp8",
+ "bbb_vp8_640x360_2mbps_30fps.vp8", "bbb_vp8_640x360_2mbps_30fps.info",
+ "bbb_vp8_640x360_2mbps_30fps_chksm.md5"},
+ {"vp9",
+ "bbb_vp9_176x144_285kbps_60fps.vp9", "bbb_vp9_176x144_285kbps_60fps.info", ""},
+ {"vp9",
+ "bbb_vp9_640x360_1600kbps_30fps.vp9", "bbb_vp9_640x360_1600kbps_30fps.info",
+ "bbb_vp9_640x360_1600kbps_30fps_chksm.md5"},
+ {"vp9",
+ "bbb_vp9_704x480_280kbps_24fps_altref_2.vp9",
+ "bbb_vp9_704x480_280kbps_24fps_altref_2.info", ""},
+ {"av01",
+ "bbb_av1_640_360.av1", "bbb_av1_640_360.info", "bbb_av1_640_360_chksum.md5"},
+ {"av01",
+ "bbb_av1_176_144.av1", "bbb_av1_176_144.info", "bbb_av1_176_144_chksm.md5"},
+};
class LinearBuffer : public C2Buffer {
public:
@@ -85,26 +128,11 @@
mLinearPool = std::make_shared<C2PooledBlockPool>(mLinearAllocator, mBlockPoolId++);
ASSERT_NE(mLinearPool, nullptr);
- mCompName = unknown_comp;
- struct StringToName {
- const char* Name;
- standardComp CompName;
- };
+ std::vector<std::unique_ptr<C2Param>> queried;
+ mComponent->query({}, {C2PortMediaTypeSetting::input::PARAM_TYPE}, C2_DONT_BLOCK, &queried);
+ ASSERT_GT(queried.size(), 0);
- const StringToName kStringToName[] = {
- {"h263", h263}, {"avc", avc}, {"mpeg2", mpeg2}, {"mpeg4", mpeg4},
- {"hevc", hevc}, {"vp8", vp8}, {"vp9", vp9}, {"av1", av1},
- };
-
- const size_t kNumStringToName = sizeof(kStringToName) / sizeof(kStringToName[0]);
-
- // Find the component type
- for (size_t i = 0; i < kNumStringToName; ++i) {
- if (strcasestr(mComponentName.c_str(), kStringToName[i].Name)) {
- mCompName = kStringToName[i].CompName;
- break;
- }
- }
+ mMime = ((C2PortMediaTypeSetting::input*)queried[0].get())->m.value;
mEos = false;
mFramesReceived = 0;
mTimestampUs = 0u;
@@ -114,11 +142,11 @@
mMd5Offset = 0;
mMd5Enable = false;
mRefMd5 = nullptr;
- if (mCompName == unknown_comp) mDisableTest = true;
C2SecureModeTuning secureModeTuning{};
mComponent->query({&secureModeTuning}, {}, C2_MAY_BLOCK, nullptr);
- if (secureModeTuning.value == C2Config::SM_READ_PROTECTED) {
+ if (secureModeTuning.value == C2Config::SM_READ_PROTECTED ||
+ secureModeTuning.value == C2Config::SM_READ_PROTECTED_WITH_ENCRYPTED) {
mDisableTest = true;
}
@@ -136,6 +164,9 @@
// Get the test parameters from GetParam call.
virtual void getParams() {}
+ void GetURLChksmForComponent(char* mURL, char* info, char* chksum, size_t streamIndex);
+ void GetURLForComponent(char* mURL, char* info, size_t streamIndex = 0);
+
/* Calculate the CKSUM for the data in inbuf */
void calc_md5_cksum(uint8_t* pu1_inbuf, uint32_t u4_stride, uint32_t u4_width,
uint32_t u4_height, uint8_t* pu1_cksum_p) {
@@ -267,18 +298,7 @@
}
}
- enum standardComp {
- h263,
- avc,
- mpeg2,
- mpeg4,
- hevc,
- vp8,
- vp9,
- av1,
- unknown_comp,
- };
-
+ std::string mMime;
std::string mInstanceName;
std::string mComponentName;
@@ -291,7 +311,6 @@
char* mRefMd5;
std::list<uint64_t> mTimestampUslist;
std::list<uint64_t> mFlushedIndices;
- standardComp mCompName;
int32_t mWorkResult;
int32_t mReorderDepth;
@@ -324,7 +343,7 @@
};
void validateComponent(const std::shared_ptr<android::Codec2Client::Component>& component,
- Codec2VideoDecHidlTest::standardComp compName, bool& disableTest) {
+ bool& disableTest) {
// Validate its a C2 Component
if (component->getName().find("c2") == std::string::npos) {
ALOGE("Not a c2 component");
@@ -351,83 +370,32 @@
return;
}
}
-
- // Validates component name
- if (compName == Codec2VideoDecHidlTest::unknown_comp) {
- ALOGE("Component InValid");
- disableTest = true;
- return;
- }
ALOGV("Component Valid");
}
// number of elementary streams per component
#define STREAM_COUNT 3
// LookUpTable of clips, metadata and chksum for component testing
-void GetURLChksmForComponent(Codec2VideoDecHidlTest::standardComp comp, char* mURL, char* info,
- char* chksum, size_t streamIndex = 1) {
- struct CompToURL {
- Codec2VideoDecHidlTest::standardComp comp;
- const char mURL[STREAM_COUNT][512];
- const char info[STREAM_COUNT][512];
- const char chksum[STREAM_COUNT][512];
- };
- ASSERT_TRUE(streamIndex < STREAM_COUNT);
-
- static const CompToURL kCompToURL[] = {
- {Codec2VideoDecHidlTest::standardComp::avc,
- {"bbb_avc_176x144_300kbps_60fps.h264", "bbb_avc_640x360_768kbps_30fps.h264", ""},
- {"bbb_avc_176x144_300kbps_60fps.info", "bbb_avc_640x360_768kbps_30fps.info", ""},
- {"bbb_avc_176x144_300kbps_60fps_chksum.md5",
- "bbb_avc_640x360_768kbps_30fps_chksum.md5", ""}},
- {Codec2VideoDecHidlTest::standardComp::hevc,
- {"bbb_hevc_176x144_176kbps_60fps.hevc", "bbb_hevc_640x360_1600kbps_30fps.hevc", ""},
- {"bbb_hevc_176x144_176kbps_60fps.info", "bbb_hevc_640x360_1600kbps_30fps.info", ""},
- {"bbb_hevc_176x144_176kbps_60fps_chksum.md5",
- "bbb_hevc_640x360_1600kbps_30fps_chksum.md5", ""}},
- {Codec2VideoDecHidlTest::standardComp::mpeg2,
- {"bbb_mpeg2_176x144_105kbps_25fps.m2v", "bbb_mpeg2_352x288_1mbps_60fps.m2v", ""},
- {"bbb_mpeg2_176x144_105kbps_25fps.info", "bbb_mpeg2_352x288_1mbps_60fps.info", ""},
- {"", "", ""}},
- {Codec2VideoDecHidlTest::standardComp::h263,
- {"", "bbb_h263_352x288_300kbps_12fps.h263", ""},
- {"", "bbb_h263_352x288_300kbps_12fps.info", ""},
- {"", "", ""}},
- {Codec2VideoDecHidlTest::standardComp::mpeg4,
- {"", "bbb_mpeg4_352x288_512kbps_30fps.m4v", ""},
- {"", "bbb_mpeg4_352x288_512kbps_30fps.info", ""},
- {"", "", ""}},
- {Codec2VideoDecHidlTest::standardComp::vp8,
- {"bbb_vp8_176x144_240kbps_60fps.vp8", "bbb_vp8_640x360_2mbps_30fps.vp8", ""},
- {"bbb_vp8_176x144_240kbps_60fps.info", "bbb_vp8_640x360_2mbps_30fps.info", ""},
- {"", "bbb_vp8_640x360_2mbps_30fps_chksm.md5", ""}},
- {Codec2VideoDecHidlTest::standardComp::vp9,
- {"bbb_vp9_176x144_285kbps_60fps.vp9", "bbb_vp9_640x360_1600kbps_30fps.vp9",
- "bbb_vp9_704x480_280kbps_24fps_altref_2.vp9"},
- {"bbb_vp9_176x144_285kbps_60fps.info", "bbb_vp9_640x360_1600kbps_30fps.info",
- "bbb_vp9_704x480_280kbps_24fps_altref_2.info"},
- {"", "bbb_vp9_640x360_1600kbps_30fps_chksm.md5", ""}},
- {Codec2VideoDecHidlTest::standardComp::av1,
- {"bbb_av1_640_360.av1", "bbb_av1_176_144.av1", ""},
- {"bbb_av1_640_360.info", "bbb_av1_176_144.info", ""},
- {"bbb_av1_640_360_chksum.md5", "bbb_av1_176_144_chksm.md5", ""}},
- };
-
- for (size_t i = 0; i < sizeof(kCompToURL) / sizeof(kCompToURL[0]); ++i) {
- if (kCompToURL[i].comp == comp) {
- strcat(mURL, kCompToURL[i].mURL[streamIndex]);
- strcat(info, kCompToURL[i].info[streamIndex]);
- strcat(chksum, kCompToURL[i].chksum[streamIndex]);
- return;
+void Codec2VideoDecHidlTestBase::GetURLChksmForComponent(char* mURL, char* info, char* chksum,
+ size_t streamIndex) {
+ int streamCount = 0;
+ for (size_t i = 0; i < kCompToURL.size(); ++i) {
+ if (mMime.find(kCompToURL[i].mime) != std::string::npos) {
+ if (streamCount == streamIndex) {
+ strcat(mURL, kCompToURL[i].mURL.c_str());
+ strcat(info, kCompToURL[i].info.c_str());
+ strcat(chksum, kCompToURL[i].chksum.c_str());
+ return;
+ }
+ streamCount++;
}
}
}
-void GetURLForComponent(Codec2VideoDecHidlTest::standardComp comp, char* mURL, char* info,
- size_t streamIndex = 1) {
+void Codec2VideoDecHidlTestBase::GetURLForComponent(char* mURL, char* info, size_t streamIndex) {
char chksum[512];
strcpy(chksum, sResourceDir.c_str());
- GetURLChksmForComponent(comp, mURL, info, chksum, streamIndex);
+ GetURLChksmForComponent(mURL, info, chksum, streamIndex);
}
void decodeNFrames(const std::shared_ptr<android::Codec2Client::Component>& component,
@@ -517,7 +485,7 @@
TEST_P(Codec2VideoDecHidlTest, validateCompName) {
if (mDisableTest) GTEST_SKIP() << "Test is disabled";
ALOGV("Checks if the given component is a valid video component");
- validateComponent(mComponent, mCompName, mDisableTest);
+ validateComponent(mComponent, mDisableTest);
ASSERT_EQ(mDisableTest, false);
}
@@ -599,7 +567,7 @@
strcpy(info, sResourceDir.c_str());
strcpy(chksum, sResourceDir.c_str());
- GetURLChksmForComponent(mCompName, mURL, info, chksum, streamIndex);
+ GetURLChksmForComponent(mURL, info, chksum, streamIndex);
if (!(strcmp(mURL, sResourceDir.c_str())) || !(strcmp(info, sResourceDir.c_str()))) {
ALOGV("Skipping Test, Stream not available");
return;
@@ -688,9 +656,11 @@
TEST_P(Codec2VideoDecHidlTest, AdaptiveDecodeTest) {
description("Adaptive Decode Test");
if (mDisableTest) GTEST_SKIP() << "Test is disabled";
- if (!(mCompName == avc || mCompName == hevc || mCompName == vp8 || mCompName == vp9 ||
- mCompName == mpeg2))
+ if (!(strcasestr(mMime.c_str(), "avc") || strcasestr(mMime.c_str(), "hevc") ||
+ strcasestr(mMime.c_str(), "vp8") || strcasestr(mMime.c_str(), "vp9") ||
+ strcasestr(mMime.c_str(), "mpeg2"))) {
return;
+ }
typedef std::unique_lock<std::mutex> ULock;
ASSERT_EQ(mComponent->start(), C2_OK);
@@ -705,7 +675,7 @@
strcpy(mURL, sResourceDir.c_str());
strcpy(info, sResourceDir.c_str());
- GetURLForComponent(mCompName, mURL, info, i % STREAM_COUNT);
+ GetURLForComponent(mURL, info, i % STREAM_COUNT);
if (!(strcmp(mURL, sResourceDir.c_str())) || !(strcmp(info, sResourceDir.c_str()))) {
ALOGV("Stream not available, skipping this index");
continue;
@@ -801,7 +771,7 @@
strcpy(mURL, sResourceDir.c_str());
strcpy(info, sResourceDir.c_str());
- GetURLForComponent(mCompName, mURL, info);
+ GetURLForComponent(mURL, info);
int32_t numCsds = populateInfoVector(info, &Info, mTimestampDevTest, &mTimestampUslist);
ASSERT_GE(numCsds, 0) << "Error in parsing input info file: " << info;
@@ -888,7 +858,7 @@
strcpy(mURL, sResourceDir.c_str());
strcpy(info, sResourceDir.c_str());
- GetURLForComponent(mCompName, mURL, info);
+ GetURLForComponent(mURL, info);
mFlushedIndices.clear();
@@ -964,7 +934,7 @@
strcpy(mURL, sResourceDir.c_str());
strcpy(info, sResourceDir.c_str());
- GetURLForComponent(mCompName, mURL, info);
+ GetURLForComponent(mURL, info);
eleInfo.open(info);
ASSERT_EQ(eleInfo.is_open(), true) << mURL << " - file not found";
@@ -1038,7 +1008,7 @@
strcpy(mURL, sResourceDir.c_str());
strcpy(info, sResourceDir.c_str());
- GetURLForComponent(mCompName, mURL, info);
+ GetURLForComponent(mURL, info);
int32_t numCsds = populateInfoVector(info, &Info, mTimestampDevTest, &mTimestampUslist);
ASSERT_GE(numCsds, 0) << "Error in parsing input info file";
@@ -1137,6 +1107,7 @@
// TODO : Video specific configuration Test
int main(int argc, char** argv) {
+ parseArgs(argc, argv);
kTestParameters = getTestParameters(C2Component::DOMAIN_VIDEO, C2Component::KIND_DECODER);
for (auto params : kTestParameters) {
kDecodeTestParameters.push_back(
@@ -1158,15 +1129,6 @@
std::make_tuple(std::get<0>(params), std::get<1>(params), "false"));
}
- // Set the resource directory based on command line args.
- // Test will fail to set up if the argument is not set.
- for (int i = 1; i < argc; i++) {
- if (strcmp(argv[i], "-P") == 0 && i < argc - 1) {
- sResourceDir = argv[i + 1];
- break;
- }
- }
-
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
diff --git a/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoEncTest.cpp b/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoEncTest.cpp
index 5bcea5b..7e35de7 100644
--- a/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoEncTest.cpp
+++ b/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoEncTest.cpp
@@ -46,9 +46,6 @@
static std::vector<std::tuple<std::string, std::string, std::string, std::string>>
kEncodeResolutionTestParameters;
-// Resource directory
-static std::string sResourceDir = "";
-
namespace {
class Codec2VideoEncHidlTestBase : public ::testing::Test {
@@ -110,7 +107,8 @@
C2SecureModeTuning secureModeTuning{};
mComponent->query({&secureModeTuning}, {}, C2_MAY_BLOCK, nullptr);
- if (secureModeTuning.value == C2Config::SM_READ_PROTECTED) {
+ if (secureModeTuning.value == C2Config::SM_READ_PROTECTED ||
+ secureModeTuning.value == C2Config::SM_READ_PROTECTED_WITH_ENCRYPTED) {
mDisableTest = true;
}
@@ -841,6 +839,7 @@
} // anonymous namespace
int main(int argc, char** argv) {
+ parseArgs(argc, argv);
kTestParameters = getTestParameters(C2Component::DOMAIN_VIDEO, C2Component::KIND_ENCODER);
for (auto params : kTestParameters) {
constexpr char const* kBoolString[] = { "false", "true" };
@@ -866,15 +865,6 @@
std::make_tuple(std::get<0>(params), std::get<1>(params), "1400", "442"));
}
- // Set the resource directory based on command line args.
- // Test will fail to set up if the argument is not set.
- for (int i = 1; i < argc; i++) {
- if (strcmp(argv[i], "-P") == 0 && i < argc - 1) {
- sResourceDir = argv[i + 1];
- break;
- }
- }
-
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
diff --git a/media/codec2/sfplugin/C2OMXNode.cpp b/media/codec2/sfplugin/C2OMXNode.cpp
index ab73245..2460490 100644
--- a/media/codec2/sfplugin/C2OMXNode.cpp
+++ b/media/codec2/sfplugin/C2OMXNode.cpp
@@ -33,12 +33,14 @@
#include <OMX_IndexExt.h>
#include <android/fdsan.h>
+#include <media/stagefright/foundation/ColorUtils.h>
#include <media/stagefright/omx/OMXUtils.h>
#include <media/stagefright/MediaErrors.h>
#include <ui/Fence.h>
#include <ui/GraphicBuffer.h>
#include <utils/Thread.h>
+#include "utils/Codec2Mapper.h"
#include "C2OMXNode.h"
namespace android {
@@ -71,6 +73,23 @@
jobs->cond.broadcast();
}
+ void setDataspace(android_dataspace dataspace) {
+ Mutexed<Jobs>::Locked jobs(mJobs);
+ ColorUtils::convertDataSpaceToV0(dataspace);
+ jobs->configUpdate.emplace_back(new C2StreamDataSpaceInfo::input(0u, dataspace));
+ int32_t standard;
+ int32_t transfer;
+ int32_t range;
+ ColorUtils::getColorConfigFromDataSpace(dataspace, &range, &standard, &transfer);
+ std::unique_ptr<C2StreamColorAspectsInfo::input> colorAspects =
+ std::make_unique<C2StreamColorAspectsInfo::input>(0u);
+ if (C2Mapper::map(standard, &colorAspects->primaries, &colorAspects->matrix)
+ && C2Mapper::map(transfer, &colorAspects->transfer)
+ && C2Mapper::map(range, &colorAspects->range)) {
+ jobs->configUpdate.push_back(std::move(colorAspects));
+ }
+ }
+
protected:
bool threadLoop() override {
constexpr nsecs_t kIntervalNs = nsecs_t(10) * 1000 * 1000; // 10ms
@@ -102,6 +121,9 @@
uniqueFds.push_back(std::move(queue.workList.front().fd1));
queue.workList.pop_front();
}
+ for (const std::unique_ptr<C2Param> ¶m : jobs->configUpdate) {
+ items.front()->input.configUpdate.emplace_back(C2Param::Copy(*param));
+ }
jobs.unlock();
for (int fenceFd : fenceFds) {
@@ -119,6 +141,7 @@
queued = true;
}
if (queued) {
+ jobs->configUpdate.clear();
return true;
}
if (i == 0) {
@@ -161,6 +184,7 @@
std::map<std::weak_ptr<Codec2Client::Component>,
Queue,
std::owner_less<std::weak_ptr<Codec2Client::Component>>> queues;
+ std::vector<std::unique_ptr<C2Param>> configUpdate;
Condition cond;
};
Mutexed<Jobs> mJobs;
@@ -172,6 +196,9 @@
mQueueThread(new QueueThread) {
android_fdsan_set_error_level(ANDROID_FDSAN_ERROR_LEVEL_WARN_ALWAYS);
mQueueThread->run("C2OMXNode", PRIORITY_AUDIO);
+
+ Mutexed<android_dataspace>::Locked ds(mDataspace);
+ *ds = HAL_DATASPACE_UNKNOWN;
}
status_t C2OMXNode::freeNode() {
@@ -459,8 +486,11 @@
android_dataspace dataSpace = (android_dataspace)msg.u.event_data.data1;
uint32_t pixelFormat = msg.u.event_data.data3;
- // TODO: set dataspace on component to see if it impacts color aspects
ALOGD("dataspace changed to %#x pixel format: %#x", dataSpace, pixelFormat);
+ mQueueThread->setDataspace(dataSpace);
+
+ Mutexed<android_dataspace>::Locked ds(mDataspace);
+ *ds = dataSpace;
return OK;
}
@@ -493,4 +523,8 @@
(void)mBufferSource->onInputBufferEmptied(bufferId, -1);
}
+android_dataspace C2OMXNode::getDataspace() {
+ return *mDataspace.lock();
+}
+
} // namespace android
diff --git a/media/codec2/sfplugin/C2OMXNode.h b/media/codec2/sfplugin/C2OMXNode.h
index 1717c96..9c04969 100644
--- a/media/codec2/sfplugin/C2OMXNode.h
+++ b/media/codec2/sfplugin/C2OMXNode.h
@@ -93,6 +93,11 @@
*/
void onInputBufferDone(c2_cntr64_t index);
+ /**
+ * Returns dataspace information from GraphicBufferSource.
+ */
+ android_dataspace getDataspace();
+
private:
std::weak_ptr<Codec2Client::Component> mComp;
sp<IOMXBufferSource> mBufferSource;
@@ -101,6 +106,7 @@
uint32_t mWidth;
uint32_t mHeight;
uint64_t mUsage;
+ Mutexed<android_dataspace> mDataspace;
// WORKAROUND: timestamp adjustment
diff --git a/media/codec2/sfplugin/CCodec.cpp b/media/codec2/sfplugin/CCodec.cpp
index c96729a..60c5901 100644
--- a/media/codec2/sfplugin/CCodec.cpp
+++ b/media/codec2/sfplugin/CCodec.cpp
@@ -211,8 +211,6 @@
(OMX_INDEXTYPE)OMX_IndexParamConsumerUsageBits,
&usage, sizeof(usage));
- // NOTE: we do not use/pass through color aspects from GraphicBufferSource as we
- // communicate that directly to the component.
mSource->configure(
mOmxNode, static_cast<hardware::graphics::common::V1_0::Dataspace>(mDataSpace));
return OK;
@@ -411,6 +409,10 @@
mNode->onInputBufferDone(index);
}
+ android_dataspace getDataspace() override {
+ return mNode->getDataspace();
+ }
+
private:
sp<HGraphicBufferSource> mSource;
sp<C2OMXNode> mNode;
@@ -1571,6 +1573,7 @@
outputFormat = config->mOutputFormat = config->mOutputFormat->dup();
if (config->mInputSurface) {
err2 = config->mInputSurface->start();
+ config->mInputSurfaceDataspace = config->mInputSurface->getDataspace();
}
buffersBoundToCodec = config->mBuffersBoundToCodec;
}
@@ -1658,6 +1661,7 @@
if (config->mInputSurface) {
config->mInputSurface->disconnect();
config->mInputSurface = nullptr;
+ config->mInputSurfaceDataspace = HAL_DATASPACE_UNKNOWN;
}
}
{
@@ -1707,6 +1711,7 @@
if (config->mInputSurface) {
config->mInputSurface->disconnect();
config->mInputSurface = nullptr;
+ config->mInputSurfaceDataspace = HAL_DATASPACE_UNKNOWN;
}
}
@@ -1966,6 +1971,39 @@
config->setParameters(comp, params, C2_MAY_BLOCK);
}
+status_t CCodec::querySupportedParameters(std::vector<std::string> *names) {
+ Mutexed<std::unique_ptr<Config>>::Locked configLocked(mConfig);
+ const std::unique_ptr<Config> &config = *configLocked;
+ return config->querySupportedParameters(names);
+}
+
+status_t CCodec::describeParameter(
+ const std::string &name, CodecParameterDescriptor *desc) {
+ Mutexed<std::unique_ptr<Config>>::Locked configLocked(mConfig);
+ const std::unique_ptr<Config> &config = *configLocked;
+ return config->describe(name, desc);
+}
+
+status_t CCodec::subscribeToParameters(const std::vector<std::string> &names) {
+ std::shared_ptr<Codec2Client::Component> comp = mState.lock()->comp;
+ if (!comp) {
+ return INVALID_OPERATION;
+ }
+ Mutexed<std::unique_ptr<Config>>::Locked configLocked(mConfig);
+ const std::unique_ptr<Config> &config = *configLocked;
+ return config->subscribeToVendorConfigUpdate(comp, names);
+}
+
+status_t CCodec::unsubscribeFromParameters(const std::vector<std::string> &names) {
+ std::shared_ptr<Codec2Client::Component> comp = mState.lock()->comp;
+ if (!comp) {
+ return INVALID_OPERATION;
+ }
+ Mutexed<std::unique_ptr<Config>>::Locked configLocked(mConfig);
+ const std::unique_ptr<Config> &config = *configLocked;
+ return config->unsubscribeFromVendorConfigUpdate(comp, names);
+}
+
void CCodec::onWorkDone(std::list<std::unique_ptr<C2Work>> &workItems) {
if (!workItems.empty()) {
Mutexed<std::list<std::unique_ptr<C2Work>>>::Locked queue(mWorkDoneQueue);
diff --git a/media/codec2/sfplugin/CCodecConfig.cpp b/media/codec2/sfplugin/CCodecConfig.cpp
index f5cc98e..ad28545 100644
--- a/media/codec2/sfplugin/CCodecConfig.cpp
+++ b/media/codec2/sfplugin/CCodecConfig.cpp
@@ -24,6 +24,7 @@
#include <C2Param.h>
#include <util/C2InterfaceHelper.h>
+#include <media/stagefright/CodecBase.h>
#include <media/stagefright/MediaCodecConstants.h>
#include "CCodecConfig.h"
@@ -290,8 +291,8 @@
std::vector<std::string> getPathsForDomain(
Domain any, Domain all = Domain::ALL) const {
std::vector<std::string> res;
- for (const std::pair<std::string, std::vector<ConfigMapper>> &el : mConfigMappers) {
- for (const ConfigMapper &cm : el.second) {
+ for (const auto &[key, mappers] : mConfigMappers) {
+ for (const ConfigMapper &cm : mappers) {
ALOGV("filtering %s %x %x %x %x", cm.path().c_str(), cm.domain(), any,
(cm.domain() & any), (cm.domain() & any & all));
if ((cm.domain() & any) && ((cm.domain() & any & all) == (any & all))) {
@@ -1061,7 +1062,7 @@
std::vector<std::string> keys;
mParamUpdater->getKeysForParamIndex(desc->index(), &keys);
for (const std::string &key : keys) {
- mVendorParamIndices.insert_or_assign(key, desc->index());
+ mVendorParams.insert_or_assign(key, desc);
}
}
}
@@ -1128,6 +1129,12 @@
insertion.first->second = std::move(p);
}
}
+ if (mInputSurface
+ && (domain & mOutputDomain)
+ && mInputSurfaceDataspace != mInputSurface->getDataspace()) {
+ changed = true;
+ mInputSurfaceDataspace = mInputSurface->getDataspace();
+ }
ALOGV("updated configuration has %zu params (%s)", mCurrentConfig.size(),
changed ? "CHANGED" : "no change");
@@ -1193,8 +1200,8 @@
const ReflectedParamUpdater::Dict &reflected,
Domain portDomain) const {
sp<AMessage> msg = new AMessage;
- for (const std::pair<std::string, std::vector<ConfigMapper>> &el : mStandardParams->getKeys()) {
- for (const ConfigMapper &cm : el.second) {
+ for (const auto &[key, mappers] : mStandardParams->getKeys()) {
+ for (const ConfigMapper &cm : mappers) {
if ((cm.domain() & portDomain) == 0 // input-output-coded-raw
|| (cm.domain() & mDomain) != mDomain // component domain + kind (these must match)
|| (cm.domain() & IS_READ) == 0) {
@@ -1218,26 +1225,26 @@
ALOGD("unexpected untyped query value for key: %s", cm.path().c_str());
continue;
}
- msg->setItem(el.first.c_str(), item);
+ msg->setItem(key.c_str(), item);
}
}
bool input = (portDomain & Domain::IS_INPUT);
std::vector<std::string> vendorKeys;
- for (const std::pair<std::string, ReflectedParamUpdater::Value> &entry : reflected) {
- auto it = mVendorParamIndices.find(entry.first);
- if (it == mVendorParamIndices.end()) {
+ for (const auto &[key, value] : reflected) {
+ auto it = mVendorParams.find(key);
+ if (it == mVendorParams.end()) {
continue;
}
- if (mSubscribedIndices.count(it->second) == 0) {
+ C2Param::Index index = it->second->index();
+ if (mSubscribedIndices.count(index) == 0) {
continue;
}
// For vendor parameters, we only care about direction
- if ((input && !it->second.forInput())
- || (!input && !it->second.forOutput())) {
+ if ((input && !index.forInput())
+ || (!input && !index.forOutput())) {
continue;
}
- const ReflectedParamUpdater::Value &value = entry.second;
C2Value c2Value;
sp<ABuffer> bufValue;
AString strValue;
@@ -1249,10 +1256,10 @@
} else if (value.find(&strValue)) {
item.set(strValue);
} else {
- ALOGD("unexpected untyped query value for key: %s", entry.first.c_str());
+ ALOGD("unexpected untyped query value for key: %s", key.c_str());
continue;
}
- msg->setItem(entry.first.c_str(), item);
+ msg->setItem(key.c_str(), item);
}
{ // convert from Codec 2.0 rect to MediaFormat rect and add crop rect if not present
@@ -1356,7 +1363,6 @@
msg->removeEntryAt(msg->findEntryByName("color-matrix"));
}
-
// calculate dataspace for raw graphic buffers if not specified by component, or if
// using surface with unspecified aspects (as those must be defaulted which may change
// the dataspace)
@@ -1394,6 +1400,23 @@
}
}
+ if (mInputSurface) {
+ android_dataspace dataspace = mInputSurface->getDataspace();
+ ColorUtils::convertDataSpaceToV0(dataspace);
+ int32_t standard;
+ ColorUtils::getColorConfigFromDataSpace(dataspace, &range, &standard, &transfer);
+ if (range != 0) {
+ msg->setInt32(KEY_COLOR_RANGE, range);
+ }
+ if (standard != 0) {
+ msg->setInt32(KEY_COLOR_STANDARD, standard);
+ }
+ if (transfer != 0) {
+ msg->setInt32(KEY_COLOR_TRANSFER, transfer);
+ }
+ msg->setInt32("android._dataspace", dataspace);
+ }
+
// HDR static info
C2HdrStaticMetadataStruct hdr;
@@ -1811,8 +1834,81 @@
status_t CCodecConfig::subscribeToAllVendorParams(
const std::shared_ptr<Codec2Client::Configurable> &configurable,
c2_blocking_t blocking) {
- for (const std::pair<std::string, C2Param::Index> &entry : mVendorParamIndices) {
- mSubscribedIndices.insert(entry.second);
+ for (const auto &[path, desc] : mVendorParams) {
+ mSubscribedIndices.insert(desc->index());
+ }
+ return subscribeToConfigUpdate(configurable, {}, blocking);
+}
+
+status_t CCodecConfig::querySupportedParameters(std::vector<std::string> *names) {
+ if (!names) {
+ return BAD_VALUE;
+ }
+ names->clear();
+ // TODO: expand to standard params
+ for (const auto &[key, desc] : mVendorParams) {
+ names->push_back(key);
+ }
+ return OK;
+}
+
+status_t CCodecConfig::describe(const std::string &name, CodecParameterDescriptor *desc) {
+ if (!desc) {
+ return BAD_VALUE;
+ }
+ // TODO: expand to standard params
+ desc->name = name;
+ switch (mParamUpdater->getTypeForKey(name)) {
+ case C2FieldDescriptor::INT32:
+ case C2FieldDescriptor::UINT32:
+ case C2FieldDescriptor::CNTR32:
+ desc->type = AMessage::kTypeInt32;
+ return OK;
+ case C2FieldDescriptor::INT64:
+ case C2FieldDescriptor::UINT64:
+ case C2FieldDescriptor::CNTR64:
+ desc->type = AMessage::kTypeInt64;
+ return OK;
+ case C2FieldDescriptor::FLOAT:
+ desc->type = AMessage::kTypeFloat;
+ return OK;
+ case C2FieldDescriptor::STRING:
+ desc->type = AMessage::kTypeString;
+ return OK;
+ case C2FieldDescriptor::BLOB:
+ desc->type = AMessage::kTypeBuffer;
+ return OK;
+ default:
+ return NAME_NOT_FOUND;
+ }
+}
+
+status_t CCodecConfig::subscribeToVendorConfigUpdate(
+ const std::shared_ptr<Codec2Client::Configurable> &configurable,
+ const std::vector<std::string> &names,
+ c2_blocking_t blocking) {
+ for (const std::string &name : names) {
+ auto it = mVendorParams.find(name);
+ if (it == mVendorParams.end()) {
+ ALOGD("%s is not a recognized vendor parameter; ignored.", name.c_str());
+ continue;
+ }
+ mSubscribedIndices.insert(it->second->index());
+ }
+ return subscribeToConfigUpdate(configurable, {}, blocking);
+}
+
+status_t CCodecConfig::unsubscribeFromVendorConfigUpdate(
+ const std::shared_ptr<Codec2Client::Configurable> &configurable,
+ const std::vector<std::string> &names,
+ c2_blocking_t blocking) {
+ for (const std::string &name : names) {
+ auto it = mVendorParams.find(name);
+ if (it == mVendorParams.end()) {
+ ALOGD("%s is not a recognized vendor parameter; ignored.", name.c_str());
+ continue;
+ }
+ mSubscribedIndices.erase(it->second->index());
}
return subscribeToConfigUpdate(configurable, {}, blocking);
}
diff --git a/media/codec2/sfplugin/CCodecConfig.h b/media/codec2/sfplugin/CCodecConfig.h
index 7e060f6..417b773 100644
--- a/media/codec2/sfplugin/CCodecConfig.h
+++ b/media/codec2/sfplugin/CCodecConfig.h
@@ -35,6 +35,7 @@
namespace android {
struct AMessage;
+struct CodecParameterDescriptor;
class NativeHandle;
struct StandardParams;
@@ -124,6 +125,7 @@
std::shared_ptr<InputSurfaceWrapper> mInputSurface;
std::unique_ptr<InputSurfaceWrapper::Config> mISConfig;
+ android_dataspace mInputSurfaceDataspace;
/// the current configuration. Updated after configure() and based on configUpdate in
/// onWorkDone
@@ -137,8 +139,8 @@
/// For now support a validation function.
std::map<C2Param::Index, LocalParamValidator> mLocalParams;
- /// Vendor field name -> index map.
- std::map<std::string, C2Param::Index> mVendorParamIndices;
+ /// Vendor field name -> desc map.
+ std::map<std::string, std::shared_ptr<C2ParamDescriptor>> mVendorParams;
std::set<std::string> mLastConfig;
@@ -326,6 +328,41 @@
return Watcher<T>(index, this);
}
+ /**
+ * Queries supported parameters and put the keys to |names|.
+ * TODO: currently this method queries vendor parameter keys only.
+ *
+ * \return OK if successful.
+ * BAD_VALUE if |names| is nullptr.
+ */
+ status_t querySupportedParameters(std::vector<std::string> *names);
+
+ /**
+ * Describe the parameter with |name|, filling the information into |desc|
+ * TODO: currently this method works only for vendor parameters.
+ *
+ * \return OK if successful.
+ * BAD_VALUE if |desc| is nullptr.
+ * NAME_NOT_FOUND if |name| is not a recognized parameter name.
+ */
+ status_t describe(const std::string &name, CodecParameterDescriptor *desc);
+
+ /**
+ * Find corresponding indices for |names| and subscribe to them.
+ */
+ status_t subscribeToVendorConfigUpdate(
+ const std::shared_ptr<Codec2Client::Configurable> &configurable,
+ const std::vector<std::string> &names,
+ c2_blocking_t blocking = C2_DONT_BLOCK);
+
+ /**
+ * Find corresponding indices for |names| and unsubscribe from them.
+ */
+ status_t unsubscribeFromVendorConfigUpdate(
+ const std::shared_ptr<Codec2Client::Configurable> &configurable,
+ const std::vector<std::string> &names,
+ c2_blocking_t blocking = C2_DONT_BLOCK);
+
private:
/// initializes the standard MediaCodec to Codec 2.0 params mapping
diff --git a/media/codec2/sfplugin/InputSurfaceWrapper.h b/media/codec2/sfplugin/InputSurfaceWrapper.h
index bb35763..3ddae01 100644
--- a/media/codec2/sfplugin/InputSurfaceWrapper.h
+++ b/media/codec2/sfplugin/InputSurfaceWrapper.h
@@ -106,6 +106,11 @@
*/
virtual void onInputBufferDone(c2_cntr64_t /* index */) {}
+ /**
+ * Returns dataspace information from GraphicBufferSource.
+ */
+ virtual android_dataspace getDataspace() { return mDataSpace; }
+
protected:
android_dataspace mDataSpace;
};
diff --git a/media/codec2/sfplugin/ReflectedParamUpdater.cpp b/media/codec2/sfplugin/ReflectedParamUpdater.cpp
index f39051b..d14b9b0 100644
--- a/media/codec2/sfplugin/ReflectedParamUpdater.cpp
+++ b/media/codec2/sfplugin/ReflectedParamUpdater.cpp
@@ -288,6 +288,20 @@
}
}
+C2FieldDescriptor::type_t ReflectedParamUpdater::getTypeForKey(
+ const std::string &key) const {
+ auto it = mMap.find(key);
+ if (it == mMap.end()) {
+ return C2FieldDescriptor::type_t(~0);
+ }
+
+ if (it->second.fieldDesc) {
+ return it->second.fieldDesc->type();
+ }
+ // whole param is exposed as a blob
+ return C2FieldDescriptor::BLOB;
+}
+
void ReflectedParamUpdater::updateParamsFromMessage(
const Dict ¶ms,
std::vector<std::unique_ptr<C2Param>> *vec /* nonnull */) const {
diff --git a/media/codec2/sfplugin/ReflectedParamUpdater.h b/media/codec2/sfplugin/ReflectedParamUpdater.h
index 752c7e4..6dcf2a3 100644
--- a/media/codec2/sfplugin/ReflectedParamUpdater.h
+++ b/media/codec2/sfplugin/ReflectedParamUpdater.h
@@ -176,6 +176,14 @@
std::vector<std::string> *keys /* nonnull */) const;
/**
+ * Get field type for the given name
+ *
+ * \param key[in] field name
+ * \return type of the field, or type_t(~0) if not found.
+ */
+ C2FieldDescriptor::type_t getTypeForKey(const std::string &name) const;
+
+ /**
* Update C2Param objects from field name and value in AMessage object.
*
* \param params[in] Dict object with field name to value pairs.
diff --git a/media/codec2/sfplugin/include/media/stagefright/CCodec.h b/media/codec2/sfplugin/include/media/stagefright/CCodec.h
index ba69d7e..ec18128 100644
--- a/media/codec2/sfplugin/include/media/stagefright/CCodec.h
+++ b/media/codec2/sfplugin/include/media/stagefright/CCodec.h
@@ -65,6 +65,12 @@
virtual void signalEndOfInputStream() override;
virtual void signalRequestIDRFrame() override;
+ virtual status_t querySupportedParameters(std::vector<std::string> *names) override;
+ virtual status_t describeParameter(
+ const std::string &name, CodecParameterDescriptor *desc) override;
+ virtual status_t subscribeToParameters(const std::vector<std::string> &names) override;
+ virtual status_t unsubscribeFromParameters(const std::vector<std::string> &names) override;
+
void initiateReleaseIfStuck();
void onWorkDone(std::list<std::unique_ptr<C2Work>> &workItems);
void onInputBufferDone(uint64_t frameIndex, size_t arrayIndex);
diff --git a/media/codec2/sfplugin/tests/CCodecConfig_test.cpp b/media/codec2/sfplugin/tests/CCodecConfig_test.cpp
index c9caa01..7c660dc 100644
--- a/media/codec2/sfplugin/tests/CCodecConfig_test.cpp
+++ b/media/codec2/sfplugin/tests/CCodecConfig_test.cpp
@@ -208,6 +208,24 @@
.withSetter(Setter<C2StreamPixelAspectRatioInfo::output>)
.build());
+ if (isEncoder) {
+ addParameter(
+ DefineParam(mInputBitrate, C2_PARAMKEY_BITRATE)
+ .withDefault(new C2StreamBitrateInfo::input(0u))
+ .withFields({C2F(mInputBitrate, value).any()})
+ .withSetter(Setter<C2StreamBitrateInfo::input>)
+ .build());
+
+ addParameter(
+ DefineParam(mOutputBitrate, C2_PARAMKEY_BITRATE)
+ .withDefault(new C2StreamBitrateInfo::output(0u))
+ .withFields({C2F(mOutputBitrate, value).any()})
+ .calculatedAs(
+ Copy<C2StreamBitrateInfo::output, C2StreamBitrateInfo::input>,
+ mInputBitrate)
+ .build());
+ }
+
// TODO: more SDK params
}
private:
@@ -221,11 +239,19 @@
std::shared_ptr<C2StreamVendorInt64Info::output> mInt64Output;
std::shared_ptr<C2PortVendorStringInfo::input> mStringInput;
std::shared_ptr<C2StreamPixelAspectRatioInfo::output> mPixelAspectRatio;
+ std::shared_ptr<C2StreamBitrateInfo::input> mInputBitrate;
+ std::shared_ptr<C2StreamBitrateInfo::output> mOutputBitrate;
template<typename T>
static C2R Setter(bool, C2P<T> &) {
return C2R::Ok();
}
+
+ template<typename ME, typename DEP>
+ static C2R Copy(bool, C2P<ME> &me, const C2P<DEP> &dep) {
+ me.set().value = dep.v.value;
+ return C2R::Ok();
+ }
};
Impl mImpl;
@@ -457,4 +483,97 @@
<< "mInputFormat = " << mConfig.mInputFormat->debugString().c_str();
}
+TEST_F(CCodecConfigTest, DataspaceUpdate) {
+ init(C2Component::DOMAIN_VIDEO, C2Component::KIND_ENCODER, MIMETYPE_VIDEO_AVC);
+
+ ASSERT_EQ(OK, mConfig.initialize(mReflector, mConfigurable));
+ class InputSurfaceStub : public InputSurfaceWrapper {
+ public:
+ ~InputSurfaceStub() override = default;
+ status_t connect(const std::shared_ptr<Codec2Client::Component> &) override {
+ return OK;
+ }
+ void disconnect() override {}
+ status_t start() override { return OK; }
+ status_t signalEndOfInputStream() override { return OK; }
+ status_t configure(Config &) override { return OK; }
+ };
+ mConfig.mInputSurface = std::make_shared<InputSurfaceStub>();
+
+ sp<AMessage> format{new AMessage};
+ format->setInt32(KEY_COLOR_RANGE, COLOR_RANGE_LIMITED);
+ format->setInt32(KEY_COLOR_STANDARD, COLOR_STANDARD_BT709);
+ format->setInt32(KEY_COLOR_TRANSFER, COLOR_TRANSFER_SDR_VIDEO);
+ format->setInt32(KEY_BIT_RATE, 100);
+
+ std::vector<std::unique_ptr<C2Param>> configUpdate;
+ ASSERT_EQ(OK, mConfig.getConfigUpdateFromSdkParams(
+ mConfigurable, format, D::ALL, C2_MAY_BLOCK, &configUpdate));
+ ASSERT_TRUE(mConfig.updateConfiguration(configUpdate, D::ALL));
+
+ int32_t range{0};
+ ASSERT_TRUE(mConfig.mOutputFormat->findInt32(KEY_COLOR_RANGE, &range))
+ << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str();
+ EXPECT_EQ(COLOR_RANGE_LIMITED, range)
+ << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str();
+
+ int32_t standard{0};
+ ASSERT_TRUE(mConfig.mOutputFormat->findInt32(KEY_COLOR_STANDARD, &standard))
+ << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str();
+ EXPECT_EQ(COLOR_STANDARD_BT709, standard)
+ << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str();
+
+ int32_t transfer{0};
+ ASSERT_TRUE(mConfig.mOutputFormat->findInt32(KEY_COLOR_TRANSFER, &transfer))
+ << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str();
+ EXPECT_EQ(COLOR_TRANSFER_SDR_VIDEO, transfer)
+ << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str();
+
+ mConfig.mInputSurface->setDataSpace(HAL_DATASPACE_BT2020_PQ);
+
+ // Dataspace from input surface should override the configured setting
+ mConfig.updateFormats(D::ALL);
+
+ ASSERT_TRUE(mConfig.mOutputFormat->findInt32(KEY_COLOR_RANGE, &range))
+ << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str();
+ EXPECT_EQ(COLOR_RANGE_FULL, range)
+ << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str();
+
+ ASSERT_TRUE(mConfig.mOutputFormat->findInt32(KEY_COLOR_STANDARD, &standard))
+ << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str();
+ EXPECT_EQ(COLOR_STANDARD_BT2020, standard)
+ << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str();
+
+ ASSERT_TRUE(mConfig.mOutputFormat->findInt32(KEY_COLOR_TRANSFER, &transfer))
+ << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str();
+ EXPECT_EQ(COLOR_TRANSFER_ST2084, transfer)
+ << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str();
+
+ // Simulate bitrate update
+ format = new AMessage;
+ format->setInt32(KEY_BIT_RATE, 200);
+ configUpdate.clear();
+ ASSERT_EQ(OK, mConfig.getConfigUpdateFromSdkParams(
+ mConfigurable, format, D::ALL, C2_MAY_BLOCK, &configUpdate));
+ ASSERT_EQ(OK, mConfig.setParameters(mConfigurable, configUpdate, C2_MAY_BLOCK));
+
+ // Color information should remain the same
+ mConfig.updateFormats(D::ALL);
+
+ ASSERT_TRUE(mConfig.mOutputFormat->findInt32(KEY_COLOR_RANGE, &range))
+ << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str();
+ EXPECT_EQ(COLOR_RANGE_FULL, range)
+ << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str();
+
+ ASSERT_TRUE(mConfig.mOutputFormat->findInt32(KEY_COLOR_STANDARD, &standard))
+ << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str();
+ EXPECT_EQ(COLOR_STANDARD_BT2020, standard)
+ << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str();
+
+ ASSERT_TRUE(mConfig.mOutputFormat->findInt32(KEY_COLOR_TRANSFER, &transfer))
+ << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str();
+ EXPECT_EQ(COLOR_TRANSFER_ST2084, transfer)
+ << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str();
+}
+
} // namespace android
diff --git a/media/codec2/sfplugin/utils/Codec2BufferUtils.cpp b/media/codec2/sfplugin/utils/Codec2BufferUtils.cpp
index bf2a07e..a54af83 100644
--- a/media/codec2/sfplugin/utils/Codec2BufferUtils.cpp
+++ b/media/codec2/sfplugin/utils/Codec2BufferUtils.cpp
@@ -121,34 +121,46 @@
if (view.crop().width != img->mWidth || view.crop().height != img->mHeight) {
return BAD_VALUE;
}
+ const uint8_t* src_y = view.data()[0];
+ const uint8_t* src_u = view.data()[1];
+ const uint8_t* src_v = view.data()[2];
+ int32_t src_stride_y = view.layout().planes[0].rowInc;
+ int32_t src_stride_u = view.layout().planes[1].rowInc;
+ int32_t src_stride_v = view.layout().planes[2].rowInc;
+ uint8_t* dst_y = imgBase + img->mPlane[0].mOffset;
+ uint8_t* dst_u = imgBase + img->mPlane[1].mOffset;
+ uint8_t* dst_v = imgBase + img->mPlane[2].mOffset;
+ int32_t dst_stride_y = img->mPlane[0].mRowInc;
+ int32_t dst_stride_u = img->mPlane[1].mRowInc;
+ int32_t dst_stride_v = img->mPlane[2].mRowInc;
+ int width = view.crop().width;
+ int height = view.crop().height;
+
if ((IsNV12(view) && IsI420(img)) || (IsI420(view) && IsNV12(img))) {
// Take shortcuts to use libyuv functions between NV12 and I420 conversion.
- const uint8_t* src_y = view.data()[0];
- const uint8_t* src_u = view.data()[1];
- const uint8_t* src_v = view.data()[2];
- int32_t src_stride_y = view.layout().planes[0].rowInc;
- int32_t src_stride_u = view.layout().planes[1].rowInc;
- int32_t src_stride_v = view.layout().planes[2].rowInc;
- uint8_t* dst_y = imgBase + img->mPlane[0].mOffset;
- uint8_t* dst_u = imgBase + img->mPlane[1].mOffset;
- uint8_t* dst_v = imgBase + img->mPlane[2].mOffset;
- int32_t dst_stride_y = img->mPlane[0].mRowInc;
- int32_t dst_stride_u = img->mPlane[1].mRowInc;
- int32_t dst_stride_v = img->mPlane[2].mRowInc;
if (IsNV12(view) && IsI420(img)) {
if (!libyuv::NV12ToI420(src_y, src_stride_y, src_u, src_stride_u, dst_y, dst_stride_y,
- dst_u, dst_stride_u, dst_v, dst_stride_v, view.crop().width,
- view.crop().height)) {
+ dst_u, dst_stride_u, dst_v, dst_stride_v, width, height)) {
return OK;
}
} else {
if (!libyuv::I420ToNV12(src_y, src_stride_y, src_u, src_stride_u, src_v, src_stride_v,
- dst_y, dst_stride_y, dst_u, dst_stride_u, view.crop().width,
- view.crop().height)) {
+ dst_y, dst_stride_y, dst_u, dst_stride_u, width, height)) {
return OK;
}
}
}
+ if (IsNV12(view) && IsNV12(img)) {
+ libyuv::CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
+ libyuv::CopyPlane(src_u, src_stride_u, dst_u, dst_stride_u, width, height / 2);
+ return OK;
+ }
+ if (IsI420(view) && IsI420(img)) {
+ libyuv::CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
+ libyuv::CopyPlane(src_u, src_stride_u, dst_u, dst_stride_u, width / 2, height / 2);
+ libyuv::CopyPlane(src_v, src_stride_v, dst_v, dst_stride_v, width / 2, height / 2);
+ return OK;
+ }
return _ImageCopy<true>(view, img, imgBase);
}
@@ -156,34 +168,47 @@
if (view.crop().width != img->mWidth || view.crop().height != img->mHeight) {
return BAD_VALUE;
}
+ const uint8_t* src_y = imgBase + img->mPlane[0].mOffset;
+ const uint8_t* src_u = imgBase + img->mPlane[1].mOffset;
+ const uint8_t* src_v = imgBase + img->mPlane[2].mOffset;
+ int32_t src_stride_y = img->mPlane[0].mRowInc;
+ int32_t src_stride_u = img->mPlane[1].mRowInc;
+ int32_t src_stride_v = img->mPlane[2].mRowInc;
+ uint8_t* dst_y = view.data()[0];
+ uint8_t* dst_u = view.data()[1];
+ uint8_t* dst_v = view.data()[2];
+ int32_t dst_stride_y = view.layout().planes[0].rowInc;
+ int32_t dst_stride_u = view.layout().planes[1].rowInc;
+ int32_t dst_stride_v = view.layout().planes[2].rowInc;
+ int width = view.crop().width;
+ int height = view.crop().height;
if ((IsNV12(img) && IsI420(view)) || (IsI420(img) && IsNV12(view))) {
// Take shortcuts to use libyuv functions between NV12 and I420 conversion.
- const uint8_t* src_y = imgBase + img->mPlane[0].mOffset;
- const uint8_t* src_u = imgBase + img->mPlane[1].mOffset;
- const uint8_t* src_v = imgBase + img->mPlane[2].mOffset;
- int32_t src_stride_y = img->mPlane[0].mRowInc;
- int32_t src_stride_u = img->mPlane[1].mRowInc;
- int32_t src_stride_v = img->mPlane[2].mRowInc;
- uint8_t* dst_y = view.data()[0];
- uint8_t* dst_u = view.data()[1];
- uint8_t* dst_v = view.data()[2];
- int32_t dst_stride_y = view.layout().planes[0].rowInc;
- int32_t dst_stride_u = view.layout().planes[1].rowInc;
- int32_t dst_stride_v = view.layout().planes[2].rowInc;
if (IsNV12(img) && IsI420(view)) {
if (!libyuv::NV12ToI420(src_y, src_stride_y, src_u, src_stride_u, dst_y, dst_stride_y,
- dst_u, dst_stride_u, dst_v, dst_stride_v, view.width(),
- view.height())) {
+ dst_u, dst_stride_u, dst_v, dst_stride_v, width, height)) {
return OK;
}
} else {
if (!libyuv::I420ToNV12(src_y, src_stride_y, src_u, src_stride_u, src_v, src_stride_v,
- dst_y, dst_stride_y, dst_u, dst_stride_u, view.width(),
- view.height())) {
+ dst_y, dst_stride_y, dst_u, dst_stride_u, width, height)) {
return OK;
}
}
}
+ if (IsNV12(img) && IsNV12(view)) {
+ // For NV12, copy Y and UV plane
+ libyuv::CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
+ libyuv::CopyPlane(src_u, src_stride_u, dst_u, dst_stride_u, width, height / 2);
+ return OK;
+ }
+ if (IsI420(img) && IsI420(view)) {
+ // For I420, copy Y, U and V plane.
+ libyuv::CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
+ libyuv::CopyPlane(src_u, src_stride_u, dst_u, dst_stride_u, width / 2, height / 2);
+ libyuv::CopyPlane(src_v, src_stride_v, dst_v, dst_stride_v, width / 2, height / 2);
+ return OK;
+ }
return _ImageCopy<false>(view, img, imgBase);
}
diff --git a/media/libmedia/tests/fuzzer/Android.bp b/media/libmedia/tests/fuzzer/Android.bp
new file mode 100644
index 0000000..c03b5b1
--- /dev/null
+++ b/media/libmedia/tests/fuzzer/Android.bp
@@ -0,0 +1,19 @@
+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_media_libmedia_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_av_media_libmedia_license"],
+}
+
+cc_fuzz {
+ name: "libmedia_metadata_fuzzer",
+ srcs: [
+ "libmedia_metadata_fuzzer.cpp",
+ ],
+ shared_libs: [
+ "libmedia",
+ "libbinder",
+ ],
+}
diff --git a/media/libmedia/tests/fuzzer/libmedia_metadata_fuzzer.cpp b/media/libmedia/tests/fuzzer/libmedia_metadata_fuzzer.cpp
new file mode 100644
index 0000000..058e4e5
--- /dev/null
+++ b/media/libmedia/tests/fuzzer/libmedia_metadata_fuzzer.cpp
@@ -0,0 +1,52 @@
+//This program fuzzes Metadata.cpp
+
+#include <stddef.h>
+#include <stdint.h>
+#include <fuzzer/FuzzedDataProvider.h>
+#include <media/Metadata.h>
+#include <binder/Parcel.h>
+
+using namespace android;
+using namespace media;
+
+static const float want_prob = 0.5;
+
+bool bytesRemain(FuzzedDataProvider *fdp);
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ FuzzedDataProvider fdp(data, size);
+ Parcel p;
+ Metadata md = Metadata(&p);
+
+ md.appendHeader();
+ while (bytesRemain(&fdp)) {
+
+ float got_prob = fdp.ConsumeProbability<float>();
+ if (!bytesRemain(&fdp)) {
+ break;
+ }
+
+ if (got_prob < want_prob) {
+ int32_t key_bool = fdp.ConsumeIntegral<int32_t>();
+ if (!bytesRemain(&fdp)) {
+ break;
+ }
+ bool val_bool = fdp.ConsumeBool();
+ md.appendBool(key_bool, val_bool);
+ } else {
+ int32_t key_int32 = fdp.ConsumeIntegral<int32_t>();
+ if (!bytesRemain(&fdp)) {
+ break;
+ }
+ bool val_int32 = fdp.ConsumeIntegral<int32_t>();
+ md.appendInt32(key_int32, val_int32);
+ }
+ md.updateLength();
+ }
+ md.resetParcel();
+ return 0;
+}
+
+bool bytesRemain(FuzzedDataProvider *fdp){
+ return fdp -> remaining_bytes() > 0;
+}
\ No newline at end of file
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 71a4ad8..224ec8b 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -5683,15 +5683,18 @@
int32_t range, standard, transfer;
convertCodecColorAspectsToPlatformAspects(aspects, &range, &standard, &transfer);
+ int32_t dsRange, dsStandard, dsTransfer;
+ getColorConfigFromDataSpace(dataSpace, &dsRange, &dsStandard, &dsTransfer);
+
// if some aspects are unspecified, use dataspace fields
if (range == 0) {
- range = (dataSpace & HAL_DATASPACE_RANGE_MASK) >> HAL_DATASPACE_RANGE_SHIFT;
+ range = dsRange;
}
if (standard == 0) {
- standard = (dataSpace & HAL_DATASPACE_STANDARD_MASK) >> HAL_DATASPACE_STANDARD_SHIFT;
+ standard = dsStandard;
}
if (transfer == 0) {
- transfer = (dataSpace & HAL_DATASPACE_TRANSFER_MASK) >> HAL_DATASPACE_TRANSFER_SHIFT;
+ transfer = dsTransfer;
}
mOutputFormat = mOutputFormat->dup(); // trigger an output format changed event
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 4406efd..8e721d4 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -1981,6 +1981,22 @@
return OK;
}
+status_t MediaCodec::querySupportedVendorParameters(std::vector<std::string> *names) {
+ return mCodec->querySupportedParameters(names);
+}
+
+status_t MediaCodec::describeParameter(const std::string &name, CodecParameterDescriptor *desc) {
+ return mCodec->describeParameter(name, desc);
+}
+
+status_t MediaCodec::subscribeToVendorParameters(const std::vector<std::string> &names) {
+ return mCodec->subscribeToParameters(names);
+}
+
+status_t MediaCodec::unsubscribeFromVendorParameters(const std::vector<std::string> &names) {
+ return mCodec->unsubscribeFromParameters(names);
+}
+
void MediaCodec::requestActivityNotification(const sp<AMessage> ¬ify) {
sp<AMessage> msg = new AMessage(kWhatRequestActivityNotification, this);
msg->setMessage("notify", notify);
diff --git a/media/libstagefright/MediaCodecSource.cpp b/media/libstagefright/MediaCodecSource.cpp
index 0f7df24..876d06c 100644
--- a/media/libstagefright/MediaCodecSource.cpp
+++ b/media/libstagefright/MediaCodecSource.cpp
@@ -775,12 +775,9 @@
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;
+ int32_t standard, transfer, range;
+ ColorUtils::getColorConfigFromDataSpace(
+ dataspace, &range, &standard, &transfer);
sp<AMessage> msg = new AMessage;
msg->setInt32(KEY_COLOR_STANDARD, standard);
msg->setInt32(KEY_COLOR_TRANSFER, transfer);
diff --git a/media/libstagefright/foundation/ColorUtils.cpp b/media/libstagefright/foundation/ColorUtils.cpp
index 070e325..3812afe 100644
--- a/media/libstagefright/foundation/ColorUtils.cpp
+++ b/media/libstagefright/foundation/ColorUtils.cpp
@@ -613,6 +613,35 @@
}
// static
+void ColorUtils::getColorConfigFromDataSpace(
+ const android_dataspace &dataspace, int32_t *range, int32_t *standard, int32_t *transfer) {
+ uint32_t gfxRange =
+ (dataspace & HAL_DATASPACE_RANGE_MASK) >> HAL_DATASPACE_RANGE_SHIFT;
+ uint32_t gfxStandard =
+ (dataspace & HAL_DATASPACE_STANDARD_MASK) >> HAL_DATASPACE_STANDARD_SHIFT;
+ uint32_t gfxTransfer =
+ (dataspace & HAL_DATASPACE_TRANSFER_MASK) >> HAL_DATASPACE_TRANSFER_SHIFT;
+
+ // assume 1-to-1 mapping to HAL values (to deal with potential vendor extensions)
+ CU::ColorRange cuRange = CU::kColorRangeUnspecified;
+ CU::ColorStandard cuStandard = CU::kColorStandardUnspecified;
+ CU::ColorTransfer cuTransfer = CU::kColorTransferUnspecified;
+ // TRICKY: use & to ensure all three mappings are completed
+ if (!(sGfxRanges.map(gfxRange, &cuRange) & sGfxStandards.map(gfxStandard, &cuStandard)
+ & sGfxTransfers.map(gfxTransfer, &cuTransfer))) {
+ ALOGW("could not safely map graphics dataspace (R:%u S:%u T:%u) to "
+ "platform color aspects (R:%u(%s) S:%u(%s) T:%u(%s)",
+ gfxRange, gfxStandard, gfxTransfer,
+ cuRange, asString(cuRange),
+ cuStandard, asString(cuStandard),
+ cuTransfer, asString(cuTransfer));
+ }
+ *range = cuRange;
+ *standard = cuStandard;
+ *transfer = cuTransfer;
+}
+
+// static
void ColorUtils::getColorConfigFromFormat(
const sp<AMessage> &format, int32_t *range, int32_t *standard, int32_t *transfer) {
if (!format->findInt32("color-range", range)) {
diff --git a/media/libstagefright/foundation/include/media/stagefright/foundation/ColorUtils.h b/media/libstagefright/foundation/include/media/stagefright/foundation/ColorUtils.h
index cd0af2b..9e3f718 100644
--- a/media/libstagefright/foundation/include/media/stagefright/foundation/ColorUtils.h
+++ b/media/libstagefright/foundation/include/media/stagefright/foundation/ColorUtils.h
@@ -156,6 +156,10 @@
// suited to blending. This requires implicit color space conversion on part of the device.
static android_dataspace getDataSpaceForColorAspects(ColorAspects &aspects, bool mayExpand);
+ // it returns the platform color configs from given |dataspace|.
+ static void getColorConfigFromDataSpace(
+ const android_dataspace &dataspace, int *range, int *standard, int *transfer);
+
// converts |dataSpace| to a V0 enum, and returns true if dataSpace is an aspect-only value
static bool convertDataSpaceToV0(android_dataspace &dataSpace);
diff --git a/media/libstagefright/include/media/stagefright/CodecBase.h b/media/libstagefright/include/media/stagefright/CodecBase.h
index dd6df90..2e98fec 100644
--- a/media/libstagefright/include/media/stagefright/CodecBase.h
+++ b/media/libstagefright/include/media/stagefright/CodecBase.h
@@ -61,6 +61,11 @@
using hardware::cas::native::V1_0::IDescrambler;
+struct CodecParameterDescriptor {
+ std::string name;
+ AMessage::Type type;
+};
+
struct CodecBase : public AHandler, /* static */ ColorUtils {
/**
* This interface defines events firing from CodecBase back to MediaCodec.
@@ -233,6 +238,64 @@
virtual void signalSetParameters(const sp<AMessage> &msg) = 0;
virtual void signalEndOfInputStream() = 0;
+ /**
+ * Query supported parameters from this instance, and fill |names| with the
+ * names of the parameters.
+ *
+ * \param names string vector to fill with supported parameters.
+ * \return OK if successful;
+ * BAD_VALUE if |names| is null;
+ * INVALID_OPERATION if already released;
+ * ERROR_UNSUPPORTED if not supported.
+ */
+ virtual status_t querySupportedParameters([[maybe_unused]] std::vector<std::string> *names) {
+ return ERROR_UNSUPPORTED;
+ }
+ /**
+ * Fill |desc| with description of the parameter with |name|.
+ *
+ * \param name name of the parameter to describe
+ * \param desc pointer to CodecParameterDescriptor to be filled
+ * \return OK if successful;
+ * BAD_VALUE if |desc| is null;
+ * NAME_NOT_FOUND if |name| is not recognized by the component;
+ * INVALID_OPERATION if already released;
+ * ERROR_UNSUPPORTED if not supported.
+ */
+ virtual status_t describeParameter(
+ [[maybe_unused]] const std::string &name,
+ [[maybe_unused]] CodecParameterDescriptor *desc) {
+ return ERROR_UNSUPPORTED;
+ }
+ /**
+ * Subscribe to parameters in |names| and get output format change event
+ * when they change.
+ * Unrecognized / already subscribed parameters are ignored.
+ *
+ * \param names names of parameters to subscribe
+ * \return OK if successful;
+ * INVALID_OPERATION if already released;
+ * ERROR_UNSUPPORTED if not supported.
+ */
+ virtual status_t subscribeToParameters(
+ [[maybe_unused]] const std::vector<std::string> &names) {
+ return ERROR_UNSUPPORTED;
+ }
+ /**
+ * Unsubscribe from parameters in |names| and no longer get
+ * output format change event when they change.
+ * Unrecognized / already unsubscribed parameters are ignored.
+ *
+ * \param names names of parameters to unsubscribe
+ * \return OK if successful;
+ * INVALID_OPERATION if already released;
+ * ERROR_UNSUPPORTED if not supported.
+ */
+ virtual status_t unsubscribeFromParameters(
+ [[maybe_unused]] const std::vector<std::string> &names) {
+ return ERROR_UNSUPPORTED;
+ }
+
typedef CodecBase *(*CreateCodecFunc)(void);
typedef PersistentSurface *(*CreateInputSurfaceFunc)(void);
diff --git a/media/libstagefright/include/media/stagefright/MediaCodec.h b/media/libstagefright/include/media/stagefright/MediaCodec.h
index a28d479..8952376 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodec.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodec.h
@@ -50,6 +50,7 @@
struct BatteryChecker;
class BufferChannelBase;
struct CodecBase;
+struct CodecParameterDescriptor;
class IBatteryStats;
struct ICrypto;
class MediaCodecBuffer;
@@ -246,6 +247,11 @@
status_t setParameters(const sp<AMessage> ¶ms);
+ status_t querySupportedVendorParameters(std::vector<std::string> *names);
+ status_t describeParameter(const std::string &name, CodecParameterDescriptor *desc);
+ status_t subscribeToVendorParameters(const std::vector<std::string> &names);
+ status_t unsubscribeFromVendorParameters(const std::vector<std::string> &names);
+
// Create a MediaCodec notification message from a list of rendered or dropped render infos
// by adding rendered frame information to a base notification message. Returns the number
// of frames that were rendered.
diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp
index cd3c743..13e2ced 100644
--- a/services/audioflinger/FastMixer.cpp
+++ b/services/audioflinger/FastMixer.cpp
@@ -353,7 +353,8 @@
#endif
//ALOGD("Eric FastMixer::onWork() mIsWarm");
} else {
- dumpState->mTimestampVerifier.discontinuity();
+ dumpState->mTimestampVerifier.discontinuity(
+ dumpState->mTimestampVerifier.DISCONTINUITY_MODE_CONTINUOUS);
// See comment in if block.
#ifdef FASTMIXER_LOG_HIST_TS
LOG_AUDIO_STATE();
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 927d87e..64e935d 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -622,7 +622,7 @@
mIoJitterMs.reset();
mLatencyMs.reset();
mProcessTimeMs.reset();
- mTimestampVerifier.discontinuity();
+ mTimestampVerifier.discontinuity(mTimestampVerifier.DISCONTINUITY_MODE_CONTINUOUS);
sp<ConfigEvent> configEvent = (ConfigEvent *)new IoConfigEvent(event, pid, portId);
sendConfigEvent_l(configEvent);
@@ -2719,7 +2719,7 @@
// the timestamp frame position to reset to 0 for direct and offload threads.
// (Out of sequence requests are ignored, since the discontinuity would be handled
// elsewhere, e.g. in flush).
- mTimestampVerifier.discontinuity();
+ mTimestampVerifier.discontinuity(mTimestampVerifier.DISCONTINUITY_MODE_ZERO);
mDrainSequence &= ~1;
mWaitWorkCV.signal();
}
@@ -3408,7 +3408,6 @@
mStandbyTimeNs = systemTime();
int64_t lastLoopCountWritten = -2; // never matches "previous" loop, when loopCount = 0.
- int64_t lastFramesWritten = -1; // track changes in timestamp server frames written
// MIXER
nsecs_t lastWarning = 0;
@@ -3444,14 +3443,6 @@
checkSilentMode_l();
- // DIRECT and OFFLOAD threads should reset frame count to zero on stop/flush
- // TODO: add confirmation checks:
- // 1) DIRECT threads and linear PCM format really resets to 0?
- // 2) Is frame count really valid if not linear pcm?
- // 3) Are all 64 bits of position returned, not just lowest 32 bits?
- if (mType == OFFLOAD || mType == DIRECT) {
- mTimestampVerifier.setDiscontinuityMode(mTimestampVerifier.DISCONTINUITY_MODE_ZERO);
- }
audio_patch_handle_t lastDownstreamPatchHandle = AUDIO_PATCH_HANDLE_NONE;
// loopCount is used for statistics and diagnostics.
@@ -3523,135 +3514,8 @@
logString = NULL;
}
- // Collect timestamp statistics for the Playback Thread types that support it.
- if (mType == MIXER
- || mType == DUPLICATING
- || mType == DIRECT
- || mType == OFFLOAD) { // no indentation
- // Gather the framesReleased counters for all active tracks,
- // and associate with the sink frames written out. We need
- // this to convert the sink timestamp to the track timestamp.
- bool kernelLocationUpdate = false;
- ExtendedTimestamp timestamp; // use private copy to fetch
- if (mStandby) {
- mTimestampVerifier.discontinuity();
- } else if (threadloop_getHalTimestamp_l(×tamp) == OK) {
- mTimestampVerifier.add(timestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL],
- timestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL],
- mSampleRate);
+ collectTimestamps_l();
- if (isTimestampCorrectionEnabled()) {
- ALOGVV("TS_BEFORE: %d %lld %lld", id(),
- (long long)timestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL],
- (long long)timestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL]);
- auto correctedTimestamp = mTimestampVerifier.getLastCorrectedTimestamp();
- timestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL]
- = correctedTimestamp.mFrames;
- timestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL]
- = correctedTimestamp.mTimeNs;
- ALOGVV("TS_AFTER: %d %lld %lld", id(),
- (long long)timestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL],
- (long long)timestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL]);
-
- // Note: Downstream latency only added if timestamp correction enabled.
- if (mDownstreamLatencyStatMs.getN() > 0) { // we have latency info.
- const int64_t newPosition =
- timestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL]
- - int64_t(mDownstreamLatencyStatMs.getMean() * mSampleRate * 1e-3);
- // prevent retrograde
- timestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL] = max(
- newPosition,
- (mTimestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL]
- - mSuspendedFrames));
- }
- }
-
- // We always fetch the timestamp here because often the downstream
- // sink will block while writing.
-
- // We keep track of the last valid kernel position in case we are in underrun
- // and the normal mixer period is the same as the fast mixer period, or there
- // is some error from the HAL.
- if (mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL] >= 0) {
- mTimestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL_LASTKERNELOK] =
- mTimestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL];
- mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL_LASTKERNELOK] =
- mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL];
-
- mTimestamp.mPosition[ExtendedTimestamp::LOCATION_SERVER_LASTKERNELOK] =
- mTimestamp.mPosition[ExtendedTimestamp::LOCATION_SERVER];
- mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_SERVER_LASTKERNELOK] =
- mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_SERVER];
- }
-
- if (timestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL] >= 0) {
- kernelLocationUpdate = true;
- } else {
- ALOGVV("getTimestamp error - no valid kernel position");
- }
-
- // copy over kernel info
- mTimestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL] =
- timestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL]
- + mSuspendedFrames; // add frames discarded when suspended
- mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL] =
- timestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL];
- } else {
- mTimestampVerifier.error();
- }
-
- // mFramesWritten for non-offloaded tracks are contiguous
- // even after standby() is called. This is useful for the track frame
- // to sink frame mapping.
- bool serverLocationUpdate = false;
- if (mFramesWritten != lastFramesWritten) {
- serverLocationUpdate = true;
- lastFramesWritten = mFramesWritten;
- }
- // Only update timestamps if there is a meaningful change.
- // Either the kernel timestamp must be valid or we have written something.
- if (kernelLocationUpdate || serverLocationUpdate) {
- if (serverLocationUpdate) {
- // use the time before we called the HAL write - it is a bit more accurate
- // to when the server last read data than the current time here.
- //
- // If we haven't written anything, mLastIoBeginNs will be -1
- // and we use systemTime().
- mTimestamp.mPosition[ExtendedTimestamp::LOCATION_SERVER] = mFramesWritten;
- mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_SERVER] = mLastIoBeginNs == -1
- ? systemTime() : mLastIoBeginNs;
- }
-
- for (const sp<Track> &t : mActiveTracks) {
- if (!t->isFastTrack()) {
- t->updateTrackFrameInfo(
- t->mAudioTrackServerProxy->framesReleased(),
- mFramesWritten,
- mSampleRate,
- mTimestamp);
- }
- }
- }
-
- if (audio_has_proportional_frames(mFormat)) {
- const double latencyMs = mTimestamp.getOutputServerLatencyMs(mSampleRate);
- if (latencyMs != 0.) { // note 0. means timestamp is empty.
- mLatencyMs.add(latencyMs);
- }
- }
-
- } // if (mType ... ) { // no indentation
-#if 0
- // logFormat example
- if (z % 100 == 0) {
- timespec ts;
- clock_gettime(CLOCK_MONOTONIC, &ts);
- LOGT("This is an integer %d, this is a float %f, this is my "
- "pid %p %% %s %t", 42, 3.14, "and this is a timestamp", ts);
- LOGT("A deceptive null-terminated string %\0");
- }
- ++z;
-#endif
saveOutputTracks();
if (mSignalPending) {
// A signal was raised while we were unlocked
@@ -4091,6 +3955,148 @@
return false;
}
+void AudioFlinger::PlaybackThread::collectTimestamps_l()
+{
+ // Collect timestamp statistics for the Playback Thread types that support it.
+ if (mType != MIXER
+ && mType != DUPLICATING
+ && mType != DIRECT
+ && mType != OFFLOAD) {
+ return;
+ }
+ if (mStandby) {
+ mTimestampVerifier.discontinuity(discontinuityForStandbyOrFlush());
+ return;
+ } else if (mHwPaused) {
+ mTimestampVerifier.discontinuity(mTimestampVerifier.DISCONTINUITY_MODE_CONTINUOUS);
+ return;
+ }
+
+ // Gather the framesReleased counters for all active tracks,
+ // and associate with the sink frames written out. We need
+ // this to convert the sink timestamp to the track timestamp.
+ bool kernelLocationUpdate = false;
+ ExtendedTimestamp timestamp; // use private copy to fetch
+
+ // Always query HAL timestamp and update timestamp verifier. In standby or pause,
+ // HAL may be draining some small duration buffered data for fade out.
+ if (threadloop_getHalTimestamp_l(×tamp) == OK) {
+ mTimestampVerifier.add(timestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL],
+ timestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL],
+ mSampleRate);
+
+ if (isTimestampCorrectionEnabled()) {
+ ALOGVV("TS_BEFORE: %d %lld %lld", id(),
+ (long long)timestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL],
+ (long long)timestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL]);
+ auto correctedTimestamp = mTimestampVerifier.getLastCorrectedTimestamp();
+ timestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL]
+ = correctedTimestamp.mFrames;
+ timestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL]
+ = correctedTimestamp.mTimeNs;
+ ALOGVV("TS_AFTER: %d %lld %lld", id(),
+ (long long)timestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL],
+ (long long)timestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL]);
+
+ // Note: Downstream latency only added if timestamp correction enabled.
+ if (mDownstreamLatencyStatMs.getN() > 0) { // we have latency info.
+ const int64_t newPosition =
+ timestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL]
+ - int64_t(mDownstreamLatencyStatMs.getMean() * mSampleRate * 1e-3);
+ // prevent retrograde
+ timestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL] = max(
+ newPosition,
+ (mTimestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL]
+ - mSuspendedFrames));
+ }
+ }
+
+ // We always fetch the timestamp here because often the downstream
+ // sink will block while writing.
+
+ // We keep track of the last valid kernel position in case we are in underrun
+ // and the normal mixer period is the same as the fast mixer period, or there
+ // is some error from the HAL.
+ if (mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL] >= 0) {
+ mTimestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL_LASTKERNELOK] =
+ mTimestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL];
+ mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL_LASTKERNELOK] =
+ mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL];
+
+ mTimestamp.mPosition[ExtendedTimestamp::LOCATION_SERVER_LASTKERNELOK] =
+ mTimestamp.mPosition[ExtendedTimestamp::LOCATION_SERVER];
+ mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_SERVER_LASTKERNELOK] =
+ mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_SERVER];
+ }
+
+ if (timestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL] >= 0) {
+ kernelLocationUpdate = true;
+ } else {
+ ALOGVV("getTimestamp error - no valid kernel position");
+ }
+
+ // copy over kernel info
+ mTimestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL] =
+ timestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL]
+ + mSuspendedFrames; // add frames discarded when suspended
+ mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL] =
+ timestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL];
+ } else {
+ mTimestampVerifier.error();
+ }
+
+ // mFramesWritten for non-offloaded tracks are contiguous
+ // even after standby() is called. This is useful for the track frame
+ // to sink frame mapping.
+ bool serverLocationUpdate = false;
+ if (mFramesWritten != mLastFramesWritten) {
+ serverLocationUpdate = true;
+ mLastFramesWritten = mFramesWritten;
+ }
+ // Only update timestamps if there is a meaningful change.
+ // Either the kernel timestamp must be valid or we have written something.
+ if (kernelLocationUpdate || serverLocationUpdate) {
+ if (serverLocationUpdate) {
+ // use the time before we called the HAL write - it is a bit more accurate
+ // to when the server last read data than the current time here.
+ //
+ // If we haven't written anything, mLastIoBeginNs will be -1
+ // and we use systemTime().
+ mTimestamp.mPosition[ExtendedTimestamp::LOCATION_SERVER] = mFramesWritten;
+ mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_SERVER] = mLastIoBeginNs == -1
+ ? systemTime() : mLastIoBeginNs;
+ }
+
+ for (const sp<Track> &t : mActiveTracks) {
+ if (!t->isFastTrack()) {
+ t->updateTrackFrameInfo(
+ t->mAudioTrackServerProxy->framesReleased(),
+ mFramesWritten,
+ mSampleRate,
+ mTimestamp);
+ }
+ }
+ }
+
+ if (audio_has_proportional_frames(mFormat)) {
+ const double latencyMs = mTimestamp.getOutputServerLatencyMs(mSampleRate);
+ if (latencyMs != 0.) { // note 0. means timestamp is empty.
+ mLatencyMs.add(latencyMs);
+ }
+ }
+#if 0
+ // logFormat example
+ if (z % 100 == 0) {
+ timespec ts;
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ LOGT("This is an integer %d, this is a float %f, this is my "
+ "pid %p %% %s %t", 42, 3.14, "and this is a timestamp", ts);
+ LOGT("A deceptive null-terminated string %\0");
+ }
+ ++z;
+#endif
+}
+
// removeTracks_l() must be called with ThreadBase::mLock held
void AudioFlinger::PlaybackThread::removeTracks_l(const Vector< sp<Track> >& tracksToRemove)
{
@@ -4137,20 +4143,15 @@
return status;
}
if ((mType == OFFLOAD || mType == DIRECT) && mOutput != NULL) {
- uint64_t position64;
- if (mOutput->getPresentationPosition(&position64, ×tamp.mTime) == OK) {
- timestamp.mPosition = (uint32_t)position64;
- if (mDownstreamLatencyStatMs.getN() > 0) {
- const uint32_t positionOffset =
- (uint32_t)(mDownstreamLatencyStatMs.getMean() * mSampleRate * 1e-3);
- if (positionOffset > timestamp.mPosition) {
- timestamp.mPosition = 0;
- } else {
- timestamp.mPosition -= positionOffset;
- }
- }
- return NO_ERROR;
+ collectTimestamps_l();
+ if (mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL] <= 0) {
+ return INVALID_OPERATION;
}
+ timestamp.mPosition = mTimestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL];
+ const int64_t timeNs = mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL];
+ timestamp.mTime.tv_sec = timeNs / NANOS_PER_SECOND;
+ timestamp.mTime.tv_nsec = timeNs - (timestamp.mTime.tv_sec * NANOS_PER_SECOND);
+ return NO_ERROR;
}
return INVALID_OPERATION;
}
@@ -5551,8 +5552,6 @@
status_t& status)
{
bool reconfig = false;
- bool a2dpDeviceChanged = false;
-
status = NO_ERROR;
AutoPark<FastMixer> park(mFastMixer);
@@ -5624,7 +5623,7 @@
}
}
- return reconfig || a2dpDeviceChanged;
+ return reconfig;
}
@@ -6085,8 +6084,6 @@
status_t& status)
{
bool reconfig = false;
- bool a2dpDeviceChanged = false;
-
status = NO_ERROR;
AudioParameter param = AudioParameter(keyValuePair);
@@ -6121,7 +6118,7 @@
}
}
- return reconfig || a2dpDeviceChanged;
+ return reconfig;
}
uint32_t AudioFlinger::DirectOutputThread::activeSleepTimeUs() const
@@ -6178,7 +6175,7 @@
mOutput->flush();
mHwPaused = false;
mFlushPending = false;
- mTimestampVerifier.discontinuity(); // DIRECT and OFFLOADED flush resets frame count.
+ mTimestampVerifier.discontinuity(discontinuityForStandbyOrFlush());
mTimestamp.clear();
}
@@ -6514,13 +6511,14 @@
track->presentationComplete(framesWritten, audioHALFrames);
track->reset();
tracksToRemove->add(track);
- // DIRECT and OFFLOADED stop resets frame counts.
+ // OFFLOADED stop resets frame counts.
if (!mUseAsyncWrite) {
// If we don't get explicit drain notification we must
// register discontinuity regardless of whether this is
// the previous (!last) or the upcoming (last) track
// to avoid skipping the discontinuity.
- mTimestampVerifier.discontinuity();
+ mTimestampVerifier.discontinuity(
+ mTimestampVerifier.DISCONTINUITY_MODE_ZERO);
}
}
} else {
@@ -7378,7 +7376,9 @@
if (mPipeSource.get() == nullptr /* don't obtain for FastCapture, could block */) {
int64_t position, time;
if (mStandby) {
- mTimestampVerifier.discontinuity();
+ mTimestampVerifier.discontinuity(audio_is_linear_pcm(mFormat) ?
+ mTimestampVerifier.DISCONTINUITY_MODE_CONTINUOUS :
+ mTimestampVerifier.DISCONTINUITY_MODE_ZERO);
} else if (mSource->getCapturePosition(&position, &time) == NO_ERROR
&& time > mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL]) {
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 709a3cc..9f65562 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -590,6 +590,11 @@
ExtendedTimestamp mTimestamp;
TimestampVerifier< // For timestamp statistics.
int64_t /* frame count */, int64_t /* time ns */> mTimestampVerifier;
+ // DIRECT and OFFLOAD threads should reset frame count to zero on stop/flush
+ // TODO: add confirmation checks:
+ // 1) DIRECT threads and linear PCM format really resets to 0?
+ // 2) Is frame count really valid if not linear pcm?
+ // 3) Are all 64 bits of position returned, not just lowest 32 bits?
// Timestamp corrected device should be a single device.
audio_devices_t mTimestampCorrectedDevice = AUDIO_DEVICE_NONE;
@@ -1023,6 +1028,8 @@
int64_t mBytesWritten;
int64_t mFramesWritten; // not reset on standby
+ int64_t mLastFramesWritten = -1; // track changes in timestamp
+ // server frames written.
int64_t mSuspendedFrames; // not reset on standby
// mHapticChannelMask and mHapticChannelCount will only be valid when the thread support
@@ -1035,6 +1042,14 @@
// copy rather than the one in AudioFlinger. This optimization saves a lock.
bool mMasterMute;
void setMasterMute_l(bool muted) { mMasterMute = muted; }
+
+ auto discontinuityForStandbyOrFlush() const { // call on threadLoop or with lock.
+ return ((mType == DIRECT && !audio_is_linear_pcm(mFormat))
+ || mType == OFFLOAD)
+ ? mTimestampVerifier.DISCONTINUITY_MODE_ZERO
+ : mTimestampVerifier.DISCONTINUITY_MODE_CONTINUOUS;
+ }
+
protected:
ActiveTracks<Track> mActiveTracks;
@@ -1081,6 +1096,8 @@
void updateMetadata_l() final;
virtual void sendMetadataToBackend_l(const StreamOutHalInterface::SourceMetadata& metadata);
+ void collectTimestamps_l();
+
// The Tracks class manages tracks added and removed from the Thread.
template <typename T>
class Tracks {
diff --git a/services/mediametrics/Android.bp b/services/mediametrics/Android.bp
index b2a0cda..f13ca74 100644
--- a/services/mediametrics/Android.bp
+++ b/services/mediametrics/Android.bp
@@ -61,6 +61,7 @@
"-bugprone-unhandled-self-assignment", // found in TimeMachine.h
"-bugprone-suspicious-string-compare", // found in TimeMachine.h
"-cert-oop54-cpp", // found in TransactionLog.h
+ "-bugprone-narrowing-conversions", // b/182410845
]
cc_defaults {