Merge "Camera: Integrate dynamic depth processing"
diff --git a/camera/CameraMetadata.cpp b/camera/CameraMetadata.cpp
index 92fe84b..15c295e 100644
--- a/camera/CameraMetadata.cpp
+++ b/camera/CameraMetadata.cpp
@@ -17,7 +17,9 @@
// #define LOG_NDEBUG 0
#define LOG_TAG "Camera2-Metadata"
+#define ATRACE_TAG ATRACE_TAG_CAMERA
#include <utils/Log.h>
+#include <utils/Trace.h>
#include <utils/Errors.h>
#include <binder/Parcel.h>
@@ -38,11 +40,13 @@
CameraMetadata::CameraMetadata(size_t entryCapacity, size_t dataCapacity) :
mLocked(false)
{
+ ATRACE_CALL();
mBuffer = allocate_camera_metadata(entryCapacity, dataCapacity);
}
CameraMetadata::CameraMetadata(const CameraMetadata &other) :
mLocked(false) {
+ ATRACE_CALL();
mBuffer = clone_camera_metadata(other.mBuffer);
}
@@ -104,6 +108,7 @@
}
void CameraMetadata::clear() {
+ ATRACE_CALL();
if (mLocked) {
ALOGE("%s: CameraMetadata is locked", __FUNCTION__);
return;
@@ -115,6 +120,7 @@
}
void CameraMetadata::acquire(camera_metadata_t *buffer) {
+ ATRACE_CALL();
if (mLocked) {
ALOGE("%s: CameraMetadata is locked", __FUNCTION__);
return;
@@ -128,6 +134,7 @@
}
void CameraMetadata::acquire(CameraMetadata &other) {
+ ATRACE_CALL();
if (mLocked) {
ALOGE("%s: CameraMetadata is locked", __FUNCTION__);
return;
@@ -136,10 +143,12 @@
}
status_t CameraMetadata::append(const CameraMetadata &other) {
+ ATRACE_CALL();
return append(other.mBuffer);
}
status_t CameraMetadata::append(const camera_metadata_t* other) {
+ ATRACE_CALL();
if (mLocked) {
ALOGE("%s: CameraMetadata is locked", __FUNCTION__);
return INVALID_OPERATION;
@@ -161,6 +170,7 @@
}
status_t CameraMetadata::sort() {
+ ATRACE_CALL();
if (mLocked) {
ALOGE("%s: CameraMetadata is locked", __FUNCTION__);
return INVALID_OPERATION;
@@ -291,6 +301,7 @@
status_t CameraMetadata::updateImpl(uint32_t tag, const void *data,
size_t data_count) {
+ ATRACE_CALL();
status_t res;
if (mLocked) {
ALOGE("%s: CameraMetadata is locked", __FUNCTION__);
@@ -348,11 +359,13 @@
}
bool CameraMetadata::exists(uint32_t tag) const {
+ ATRACE_CALL();
camera_metadata_ro_entry entry;
return find_camera_metadata_ro_entry(mBuffer, tag, &entry) == 0;
}
camera_metadata_entry_t CameraMetadata::find(uint32_t tag) {
+ ATRACE_CALL();
status_t res;
camera_metadata_entry entry;
if (mLocked) {
@@ -369,6 +382,7 @@
}
camera_metadata_ro_entry_t CameraMetadata::find(uint32_t tag) const {
+ ATRACE_CALL();
status_t res;
camera_metadata_ro_entry entry;
res = find_camera_metadata_ro_entry(mBuffer, tag, &entry);
@@ -380,6 +394,7 @@
}
status_t CameraMetadata::erase(uint32_t tag) {
+ ATRACE_CALL();
camera_metadata_entry_t entry;
status_t res;
if (mLocked) {
@@ -410,6 +425,7 @@
status_t CameraMetadata::removePermissionEntries(metadata_vendor_id_t vendorId,
std::vector<int32_t> *tagsRemoved) {
+ ATRACE_CALL();
uint32_t tagCount = 0;
std::vector<uint32_t> tagsToRemove;
@@ -486,6 +502,7 @@
}
status_t CameraMetadata::resizeIfNeeded(size_t extraEntries, size_t extraData) {
+ ATRACE_CALL();
if (mBuffer == NULL) {
mBuffer = allocate_camera_metadata(extraEntries * 2, extraData * 2);
if (mBuffer == NULL) {
@@ -525,7 +542,7 @@
status_t CameraMetadata::readFromParcel(const Parcel& data,
camera_metadata_t** out) {
-
+ ATRACE_CALL();
status_t err = OK;
camera_metadata_t* metadata = NULL;
@@ -616,6 +633,7 @@
status_t CameraMetadata::writeToParcel(Parcel& data,
const camera_metadata_t* metadata) {
+ ATRACE_CALL();
status_t res = OK;
/**
@@ -710,7 +728,7 @@
}
status_t CameraMetadata::readFromParcel(const Parcel *parcel) {
-
+ ATRACE_CALL();
ALOGV("%s: parcel = %p", __FUNCTION__, parcel);
status_t res = OK;
@@ -742,7 +760,7 @@
}
status_t CameraMetadata::writeToParcel(Parcel *parcel) const {
-
+ ATRACE_CALL();
ALOGV("%s: parcel = %p", __FUNCTION__, parcel);
if (parcel == NULL) {
@@ -771,7 +789,7 @@
status_t CameraMetadata::getTagFromName(const char *name,
const VendorTagDescriptor* vTags, uint32_t *tag) {
-
+ ATRACE_CALL();
if (name == nullptr || tag == nullptr) return BAD_VALUE;
size_t nameLength = strlen(name);
diff --git a/media/extractors/aac/Android.bp b/media/extractors/aac/Android.bp
index a58167a..53b394f 100644
--- a/media/extractors/aac/Android.bp
+++ b/media/extractors/aac/Android.bp
@@ -1,4 +1,4 @@
-cc_library_shared {
+cc_library {
srcs: ["AACExtractor.cpp"],
diff --git a/media/extractors/amr/Android.bp b/media/extractors/amr/Android.bp
index 4bd933d..cd76062 100644
--- a/media/extractors/amr/Android.bp
+++ b/media/extractors/amr/Android.bp
@@ -1,4 +1,4 @@
-cc_library_shared {
+cc_library {
srcs: ["AMRExtractor.cpp"],
diff --git a/media/extractors/flac/Android.bp b/media/extractors/flac/Android.bp
index 3a3d051..c669b34 100644
--- a/media/extractors/flac/Android.bp
+++ b/media/extractors/flac/Android.bp
@@ -1,4 +1,4 @@
-cc_library_shared {
+cc_library {
srcs: ["FLACExtractor.cpp"],
diff --git a/media/extractors/midi/Android.bp b/media/extractors/midi/Android.bp
index d36cb49..40c91e7 100644
--- a/media/extractors/midi/Android.bp
+++ b/media/extractors/midi/Android.bp
@@ -1,4 +1,4 @@
-cc_library_shared {
+cc_library {
srcs: ["MidiExtractor.cpp"],
diff --git a/media/extractors/mkv/Android.bp b/media/extractors/mkv/Android.bp
index 38821fd..942c88a 100644
--- a/media/extractors/mkv/Android.bp
+++ b/media/extractors/mkv/Android.bp
@@ -1,4 +1,4 @@
-cc_library_shared {
+cc_library {
srcs: ["MatroskaExtractor.cpp"],
diff --git a/media/extractors/mp3/Android.bp b/media/extractors/mp3/Android.bp
index 4e2f248..6f02b0f 100644
--- a/media/extractors/mp3/Android.bp
+++ b/media/extractors/mp3/Android.bp
@@ -1,4 +1,4 @@
-cc_library_shared {
+cc_library {
srcs: [
"MP3Extractor.cpp",
diff --git a/media/extractors/mp4/Android.bp b/media/extractors/mp4/Android.bp
index 1b308aa..d9f11fc 100644
--- a/media/extractors/mp4/Android.bp
+++ b/media/extractors/mp4/Android.bp
@@ -35,7 +35,7 @@
compile_multilib: "first",
}
-cc_library_shared {
+cc_library {
name: "libmp4extractor",
diff --git a/media/extractors/mpeg2/Android.bp b/media/extractors/mpeg2/Android.bp
index 8638cdc..83e6d73 100644
--- a/media/extractors/mpeg2/Android.bp
+++ b/media/extractors/mpeg2/Android.bp
@@ -1,4 +1,4 @@
-cc_library_shared {
+cc_library {
srcs: [
"ExtractorBundle.cpp",
diff --git a/media/extractors/ogg/Android.bp b/media/extractors/ogg/Android.bp
index 604ec59..e661b5d 100644
--- a/media/extractors/ogg/Android.bp
+++ b/media/extractors/ogg/Android.bp
@@ -1,4 +1,4 @@
-cc_library_shared {
+cc_library {
srcs: ["OggExtractor.cpp"],
diff --git a/media/extractors/wav/Android.bp b/media/extractors/wav/Android.bp
index 7e89271..51e3c31 100644
--- a/media/extractors/wav/Android.bp
+++ b/media/extractors/wav/Android.bp
@@ -1,4 +1,4 @@
-cc_library_shared {
+cc_library {
srcs: ["WAVExtractor.cpp"],
diff --git a/media/libmediametrics/include/MediaMetricsItem.h b/media/libmediametrics/include/MediaMetricsItem.h
index f844c82..dbbcaf9 100644
--- a/media/libmediametrics/include/MediaMetricsItem.h
+++ b/media/libmediametrics/include/MediaMetricsItem.h
@@ -128,9 +128,28 @@
template<size_t N>
static inline bool startsWith(const std::string &s, const char (&comp)[N]) {
- return !strncmp(s.c_str(), comp, N-1);
+ return !strncmp(s.c_str(), comp, N - 1);
}
+static inline bool startsWith(const std::string& s, const std::string& comp) {
+ return !strncmp(s.c_str(), comp.c_str(), comp.size() - 1);
+}
+
+/**
+ * Defers a function to run in the destructor.
+ *
+ * This helper class is used to log results on exit of a method.
+ */
+class Defer {
+public:
+ template <typename U>
+ Defer(U &&f) : mThunk(std::forward<U>(f)) {}
+ ~Defer() { mThunk(); }
+
+private:
+ const std::function<void()> mThunk;
+};
+
/**
* Media Metrics BaseItem
*
@@ -616,9 +635,167 @@
*
* The Item is designed for the service as it has getters.
*/
-class Item : public mediametrics::BaseItem {
+class Item final : public mediametrics::BaseItem {
public:
+ class Prop {
+ public:
+ using Elem = std::variant<
+ std::monostate, // kTypeNone
+ int32_t, // kTypeInt32
+ int64_t, // kTypeInt64
+ double, // kTypeDouble
+ std::string, // kTypeCString
+ std::pair<int64_t, int64_t> // kTypeRate
+ >;
+
+ Prop() = default;
+ Prop(const Prop& other) {
+ *this = other;
+ }
+ Prop& operator=(const Prop& other) {
+ mName = other.mName;
+ mElem = other.mElem;
+ return *this;
+ }
+ Prop(Prop&& other) {
+ *this = std::move(other);
+ }
+ Prop& operator=(Prop&& other) {
+ mName = std::move(other.mName);
+ mElem = std::move(other.mElem);
+ return *this;
+ }
+
+ bool operator==(const Prop& other) const {
+ return mName == other.mName && mElem == other.mElem;
+ }
+ bool operator!=(const Prop& other) const {
+ return !(*this == other);
+ }
+
+ void clear() {
+ mName.clear();
+ mElem = std::monostate{};
+ }
+ void clearValue() {
+ mElem = std::monostate{};
+ }
+
+ const char *getName() const {
+ return mName.c_str();
+ }
+
+ void swap(Prop& other) {
+ std::swap(mName, other.mName);
+ std::swap(mElem, other.mElem);
+ }
+
+ void setName(const char *name) {
+ mName = name;
+ }
+
+ bool isNamed(const char *name) const {
+ return mName == name;
+ }
+
+ template <typename T> void visit(T f) const {
+ std::visit(f, mElem);
+ }
+
+ template <typename T> bool get(T *value) const {
+ auto pval = std::get_if<T>(&mElem);
+ if (pval != nullptr) {
+ *value = *pval;
+ return true;
+ }
+ return false;
+ }
+
+ const Elem& get() const {
+ return mElem;
+ }
+
+ template <typename T> void set(const T& value) {
+ mElem = value;
+ }
+
+ template <typename T> void add(const T& value) {
+ auto pval = std::get_if<T>(&mElem);
+ if (pval != nullptr) {
+ *pval += value;
+ } else {
+ mElem = value;
+ }
+ }
+
+ template <> void add(const std::pair<int64_t, int64_t>& value) {
+ auto pval = std::get_if<std::pair<int64_t, int64_t>>(&mElem);
+ if (pval != nullptr) {
+ pval->first += value.first;
+ pval->second += value.second;
+ } else {
+ mElem = value;
+ }
+ }
+
+ status_t writeToParcel(Parcel *parcel) const {
+ return std::visit([this, parcel](auto &value) {
+ return BaseItem::writeToParcel(mName.c_str(), value, parcel);}, mElem);
+ }
+
+ void toStringBuffer(char *buffer, size_t length) const {
+ return std::visit([this, buffer, length](auto &value) {
+ BaseItem::toStringBuffer(mName.c_str(), value, buffer, length);}, mElem);
+ }
+
+ size_t getByteStringSize() const {
+ return std::visit([this](auto &value) {
+ return BaseItem::sizeOfByteString(mName.c_str(), value);}, mElem);
+ }
+
+ status_t writeToByteString(char **bufferpptr, char *bufferptrmax) const {
+ return std::visit([this, bufferpptr, bufferptrmax](auto &value) {
+ return BaseItem::writeToByteString(mName.c_str(), value, bufferpptr, bufferptrmax);
+ }, mElem);
+ }
+
+ status_t readFromParcel(const Parcel& data);
+
+ status_t readFromByteString(const char **bufferpptr, const char *bufferptrmax);
+
+ private:
+ std::string mName;
+ Elem mElem;
+ };
+
+ // Iteration of props within item
+ class iterator {
+ public:
+ iterator(const std::map<std::string, Prop>::const_iterator &_it) : it(_it) { }
+ iterator &operator++() {
+ ++it;
+ return *this;
+ }
+ bool operator!=(iterator &other) const {
+ return it != other.it;
+ }
+ const Prop &operator*() const {
+ return it->second;
+ }
+
+ private:
+ std::map<std::string, Prop>::const_iterator it;
+ };
+
+ iterator begin() const {
+ return iterator(mProps.cbegin());
+ }
+
+ iterator end() const {
+ return iterator(mProps.cend());
+ }
+
enum {
PROTO_V0 = 0,
PROTO_FIRST = PROTO_V0,
@@ -632,15 +809,22 @@
: mKey(key) { }
Item() = default;
+ // We enable default copy and move constructors and make this class final
+ // to prevent a derived class; this avoids possible data slicing.
+ Item(const Item& other) = default;
+ Item(Item&& other) = default;
+ Item& operator=(const Item& other) = default;
+ Item& operator=(Item&& other) = default;
+
bool operator==(const Item& other) const {
- if (mPid != other.mPid
- || mUid != other.mUid
- || mPkgName != other.mPkgName
- || mPkgVersionCode != other.mPkgVersionCode
- || mKey != other.mKey
- || mTimestamp != other.mTimestamp
- || mProps != other.mProps) return false;
- return true;
+ return mPid == other.mPid
+ && mUid == other.mUid
+ && mPkgName == other.mPkgName
+ && mPkgVersionCode == other.mPkgVersionCode
+ && mKey == other.mKey
+ && mTimestamp == other.mTimestamp
+ && mProps == other.mProps
+ ;
}
bool operator!=(const Item& other) const {
return !(*this == other);
@@ -770,6 +954,11 @@
return get(key, value);
}
+ const Prop::Elem* get(const char *key) const {
+ const Prop *prop = findProp(key);
+ return prop == nullptr ? nullptr : &prop->get();
+ }
+
// Deliver the item to MediaMetrics
bool selfrecord();
@@ -820,164 +1009,6 @@
int32_t writeToParcel0(Parcel *) const;
int32_t readFromParcel0(const Parcel&);
-public:
-
- class Prop {
- public:
- using Elem = std::variant<
- std::monostate, // kTypeNone
- int32_t, // kTypeInt32
- int64_t, // kTypeInt64
- double, // kTypeDouble
- std::string, // kTypeCString
- std::pair<int64_t, int64_t> // kTypeRate
- >;
-
- Prop() = default;
- Prop(const Prop& other) {
- *this = other;
- }
- Prop& operator=(const Prop& other) {
- mName = other.mName;
- mElem = other.mElem;
- return *this;
- }
- Prop(Prop&& other) {
- *this = std::move(other);
- }
- Prop& operator=(Prop&& other) {
- mName = std::move(other.mName);
- mElem = std::move(other.mElem);
- return *this;
- }
-
- bool operator==(const Prop& other) const {
- return mName == other.mName && mElem == other.mElem;
- }
- bool operator!=(const Prop& other) const {
- return !(*this == other);
- }
-
- void clear() {
- mName.clear();
- mElem = std::monostate{};
- }
- void clearValue() {
- mElem = std::monostate{};
- }
-
- const char *getName() const {
- return mName.c_str();
- }
-
- void swap(Prop& other) {
- std::swap(mName, other.mName);
- std::swap(mElem, other.mElem);
- }
-
- void setName(const char *name) {
- mName = name;
- }
-
- bool isNamed(const char *name) const {
- return mName == name;
- }
-
- template <typename T> void visit(T f) const {
- std::visit(f, mElem);
- }
-
- template <typename T> bool get(T *value) const {
- auto pval = std::get_if<T>(&mElem);
- if (pval != nullptr) {
- *value = *pval;
- return true;
- }
- return false;
- }
-
- template <typename T> void set(const T& value) {
- mElem = value;
- }
-
- template <typename T> void add(const T& value) {
- auto pval = std::get_if<T>(&mElem);
- if (pval != nullptr) {
- *pval += value;
- } else {
- mElem = value;
- }
- }
-
- template <> void add(const std::pair<int64_t, int64_t>& value) {
- auto pval = std::get_if<std::pair<int64_t, int64_t>>(&mElem);
- if (pval != nullptr) {
- pval->first += value.first;
- pval->second += value.second;
- } else {
- mElem = value;
- }
- }
-
- status_t writeToParcel(Parcel *parcel) const {
- return std::visit([this, parcel](auto &value) {
- return BaseItem::writeToParcel(mName.c_str(), value, parcel);}, mElem);
- }
-
- void toStringBuffer(char *buffer, size_t length) const {
- return std::visit([this, buffer, length](auto &value) {
- BaseItem::toStringBuffer(mName.c_str(), value, buffer, length);}, mElem);
- }
-
- size_t getByteStringSize() const {
- return std::visit([this](auto &value) {
- return BaseItem::sizeOfByteString(mName.c_str(), value);}, mElem);
- }
-
- status_t writeToByteString(char **bufferpptr, char *bufferptrmax) const {
- return std::visit([this, bufferpptr, bufferptrmax](auto &value) {
- return BaseItem::writeToByteString(mName.c_str(), value, bufferpptr, bufferptrmax);
- }, mElem);
- }
-
- status_t readFromParcel(const Parcel& data);
-
- status_t readFromByteString(const char **bufferpptr, const char *bufferptrmax);
-
- private:
- std::string mName;
- Elem mElem;
- };
-
- // Iteration of props within item
- class iterator {
- public:
- iterator(const std::map<std::string, Prop>::const_iterator &_it) : it(_it) { }
- iterator &operator++() {
- ++it;
- return *this;
- }
- bool operator!=(iterator &other) const {
- return it != other.it;
- }
- const Prop &operator*() const {
- return it->second;
- }
-
- private:
- std::map<std::string, Prop>::const_iterator it;
- };
-
- iterator begin() const {
- return iterator(mProps.cbegin());
- }
-
- iterator end() const {
- return iterator(mProps.cend());
- }
-
-private:
-
const Prop *findProp(const char *key) const {
auto it = mProps.find(key);
return it != mProps.end() ? &it->second : nullptr;
@@ -991,6 +1022,7 @@
return prop;
}
+ // Changes to member variables below require changes to clear().
pid_t mPid = -1;
uid_t mUid = -1;
std::string mPkgName;
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index 743a3e4..c88a82a 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -358,6 +358,7 @@
ItemRefs mDimgRefs;
Vector<uint16_t> mExifList;
uint16_t mImageItemId;
+ uint16_t mItemIdBase;
int32_t mIsPrimary;
int32_t mWidth, mHeight;
int32_t mTileWidth, mTileHeight;
@@ -507,6 +508,7 @@
mPrimaryItemId = 0;
mAssociationEntryCount = 0;
mNumGrids = 0;
+ mNextItemId = kItemIdBase;
mHasRefs = false;
mPreAllocFirstTime = true;
mPrevAllTracksTotalMetaDataSizeEstimate = 0;
@@ -1930,6 +1932,7 @@
mRotation(0),
mDimgRefs("dimg"),
mImageItemId(0),
+ mItemIdBase(0),
mIsPrimary(0),
mWidth(0),
mHeight(0),
@@ -2162,8 +2165,14 @@
}
if (isExif) {
- mExifList.push_back(mOwner->addItem_l({
+ uint16_t exifItemId;
+ if (mOwner->reserveItemId_l(1, &exifItemId) != OK) {
+ return;
+ }
+
+ mExifList.push_back(mOwner->addItem_l({
.itemType = "Exif",
+ .itemId = exifItemId,
.isPrimary = false,
.isHidden = false,
.offset = (uint32_t)offset,
@@ -2212,6 +2221,7 @@
if (hasGrid) {
mDimgRefs.value.push_back(mOwner->addItem_l({
.itemType = "hvc1",
+ .itemId = mItemIdBase++,
.isPrimary = false,
.isHidden = true,
.offset = (uint32_t)offset,
@@ -2234,6 +2244,7 @@
}
mImageItemId = mOwner->addItem_l({
.itemType = "grid",
+ .itemId = mItemIdBase++,
.isPrimary = (mIsPrimary != 0),
.isHidden = false,
.rows = (uint32_t)mGridRows,
@@ -2246,6 +2257,7 @@
} else {
mImageItemId = mOwner->addItem_l({
.itemType = "hvc1",
+ .itemId = mItemIdBase++,
.isPrimary = (mIsPrimary != 0),
.isHidden = false,
.offset = (uint32_t)offset,
@@ -2601,6 +2613,22 @@
params->findInt32(kKeyRotation, &rotationDegrees)) {
mRotation = rotationDegrees;
}
+ if (mIsHeic) {
+ // Reserve the item ids, so that the item ids are ordered in the same
+ // order that the image tracks are added.
+ // If we leave the item ids to be assigned when the sample is written out,
+ // the original track order may not be preserved, if two image tracks
+ // have data around the same time. (This could happen especially when
+ // we're encoding with single tile.) The reordering may be undesirable,
+ // even if the file is well-formed and the primary picture is correct.
+
+ // Reserve item ids for samples + grid
+ size_t numItemsToReserve = mNumTiles + (mNumTiles > 1);
+ status_t err = mOwner->reserveItemId_l(numItemsToReserve, &mItemIdBase);
+ if (err != OK) {
+ return err;
+ }
+ }
initTrackingProgressStatus(params);
@@ -4680,9 +4708,11 @@
}
writeInt16((uint16_t)itemCount);
- for (size_t i = 0; i < itemCount; i++) {
- writeInt16(mItems[i].itemId);
- bool isGrid = mItems[i].isGrid();
+ for (auto it = mItems.begin(); it != mItems.end(); it++) {
+ ItemInfo &item = it->second;
+
+ writeInt16(item.itemId);
+ bool isGrid = item.isGrid();
writeInt16(isGrid ? 1 : 0); // construction_method
writeInt16(0); // data_reference_index = 0
@@ -4693,8 +4723,8 @@
writeInt32(mNumGrids++ * 8);
writeInt32(8);
} else {
- writeInt32(mItems[i].offset);
- writeInt32(mItems[i].size);
+ writeInt32(item.offset);
+ writeInt32(item.size);
}
}
endBox();
@@ -4723,9 +4753,11 @@
}
writeInt16((uint16_t)itemCount);
- for (size_t i = 0; i < itemCount; i++) {
- writeInfeBox(mItems[i].itemId, mItems[i].itemType,
- (mItems[i].isImage() && mItems[i].isHidden) ? 1 : 0);
+ for (auto it = mItems.begin(); it != mItems.end(); it++) {
+ ItemInfo &item = it->second;
+
+ writeInfeBox(item.itemId, item.itemType,
+ (item.isImage() && item.isHidden) ? 1 : 0);
}
endBox();
@@ -4734,20 +4766,22 @@
void MPEG4Writer::writeIdatBox() {
beginBox("idat");
- for (size_t i = 0; i < mItems.size(); i++) {
- if (mItems[i].isGrid()) {
+ for (auto it = mItems.begin(); it != mItems.end(); it++) {
+ ItemInfo &item = it->second;
+
+ if (item.isGrid()) {
writeInt8(0); // version
// flags == 1 means 32-bit width,height
- int8_t flags = (mItems[i].width > 65535 || mItems[i].height > 65535);
+ int8_t flags = (item.width > 65535 || item.height > 65535);
writeInt8(flags);
- writeInt8(mItems[i].rows - 1);
- writeInt8(mItems[i].cols - 1);
+ writeInt8(item.rows - 1);
+ writeInt8(item.cols - 1);
if (flags) {
- writeInt32(mItems[i].width);
- writeInt32(mItems[i].height);
+ writeInt32(item.width);
+ writeInt32(item.height);
} else {
- writeInt16((uint16_t)mItems[i].width);
- writeInt16((uint16_t)mItems[i].height);
+ writeInt16((uint16_t)item.width);
+ writeInt16((uint16_t)item.height);
}
}
}
@@ -4759,11 +4793,13 @@
beginBox("iref");
writeInt32(0); // Version = 0, Flags = 0
{
- for (size_t i = 0; i < mItems.size(); i++) {
- for (size_t r = 0; r < mItems[i].refsList.size(); r++) {
- const ItemRefs &refs = mItems[i].refsList[r];
+ for (auto it = mItems.begin(); it != mItems.end(); it++) {
+ ItemInfo &item = it->second;
+
+ for (size_t r = 0; r < item.refsList.size(); r++) {
+ const ItemRefs &refs = item.refsList[r];
beginBox(refs.key);
- writeInt16(mItems[i].itemId);
+ writeInt16(item.itemId);
size_t refCount = refs.value.size();
if (refCount > 65535) {
ALOGW("too many entries in %s", refs.key);
@@ -4838,12 +4874,14 @@
writeInt32(flags); // Version = 0
writeInt32(mAssociationEntryCount);
- for (size_t itemIndex = 0; itemIndex < mItems.size(); itemIndex++) {
- const Vector<uint16_t> &properties = mItems[itemIndex].properties;
+ for (auto it = mItems.begin(); it != mItems.end(); it++) {
+ ItemInfo &item = it->second;
+
+ const Vector<uint16_t> &properties = item.properties;
if (properties.empty()) {
continue;
}
- writeInt16(mItems[itemIndex].itemId);
+ writeInt16(item.itemId);
size_t entryCount = properties.size();
if (entryCount > 255) {
@@ -4873,19 +4911,21 @@
// patch up the mPrimaryItemId and count items with prop associations
uint16_t firstVisibleItemId = 0;
uint16_t firstImageItemId = 0;
- for (size_t index = 0; index < mItems.size(); index++) {
- if (!mItems[index].isImage()) continue;
+ for (auto it = mItems.begin(); it != mItems.end(); it++) {
+ ItemInfo &item = it->second;
- if (mItems[index].isPrimary) {
- mPrimaryItemId = mItems[index].itemId;
+ if (!item.isImage()) continue;
+
+ if (item.isPrimary) {
+ mPrimaryItemId = item.itemId;
}
if (!firstImageItemId) {
- firstImageItemId = mItems[index].itemId;
+ firstImageItemId = item.itemId;
}
- if (!firstVisibleItemId && !mItems[index].isHidden) {
- firstVisibleItemId = mItems[index].itemId;
+ if (!firstVisibleItemId && !item.isHidden) {
+ firstVisibleItemId = item.itemId;
}
- if (!mItems[index].properties.empty()) {
+ if (!item.properties.empty()) {
mAssociationEntryCount++;
}
}
@@ -4939,15 +4979,25 @@
return mProperties.size();
}
+status_t MPEG4Writer::reserveItemId_l(size_t numItems, uint16_t *itemIdBase) {
+ if (numItems > UINT16_MAX - mNextItemId) {
+ ALOGE("couldn't reserve item ids for %zu items", numItems);
+ return ERROR_OUT_OF_RANGE;
+ }
+ *itemIdBase = mNextItemId;
+ mNextItemId += numItems;
+ return OK;
+}
+
uint16_t MPEG4Writer::addItem_l(const ItemInfo &info) {
ALOGV("addItem_l: type %s, offset %u, size %u",
info.itemType, info.offset, info.size);
- size_t index = mItems.size();
- mItems.push_back(info);
+ if (info.itemId < kItemIdBase || info.itemId >= mNextItemId) {
+ ALOGW("Item id %u is used without reservation!", info.itemId);
+ }
- // make the item id start at kItemIdBase
- mItems.editItemAt(index).itemId = index + kItemIdBase;
+ mItems[info.itemId] = info;
#if (LOG_NDEBUG==0)
if (!info.properties.empty()) {
@@ -4958,24 +5008,28 @@
}
str.append(info.properties[i]);
}
- ALOGV("addItem_l: id %d, properties: %s", mItems[index].itemId, str.c_str());
+ ALOGV("addItem_l: id %d, properties: %s", info.itemId, str.c_str());
}
#endif // (LOG_NDEBUG==0)
- return mItems[index].itemId;
+ return info.itemId;
}
void MPEG4Writer::addRefs_l(uint16_t itemId, const ItemRefs &refs) {
if (refs.value.empty()) {
return;
}
- if (itemId < kItemIdBase) {
- ALOGW("itemId shouldn't be smaller than kItemIdBase");
+ if (itemId < kItemIdBase || itemId >= mNextItemId) {
+ ALOGW("itemId %u for ref is invalid!", itemId);
return;
}
- size_t index = itemId - kItemIdBase;
- mItems.editItemAt(index).refsList.push_back(refs);
+ auto it = mItems.find(itemId);
+ if (it == mItems.end()) {
+ ALOGW("itemId %u was not added yet", itemId);
+ return;
+ }
+ it->second.refsList.push_back(refs);
mHasRefs = true;
}
diff --git a/media/libstagefright/codecs/amrnb/dec/test/AmrnbDecTestEnvironment.h b/media/libstagefright/codecs/amrnb/dec/test/AmrnbDecTestEnvironment.h
new file mode 100644
index 0000000..0344ac5
--- /dev/null
+++ b/media/libstagefright/codecs/amrnb/dec/test/AmrnbDecTestEnvironment.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __AMRNBDEC_TEST_ENVIRONMENT_H__
+#define __AMRNBDEC_TEST_ENVIRONMENT_H__
+
+#include <gtest/gtest.h>
+
+#include <getopt.h>
+
+using namespace std;
+
+class AmrnbDecTestEnvironment : public ::testing::Environment {
+ public:
+ AmrnbDecTestEnvironment() : res("/data/local/tmp/") {}
+
+ // Parses the command line arguments
+ int initFromOptions(int argc, char **argv);
+
+ void setRes(const char *_res) { res = _res; }
+
+ const string getRes() const { return res; }
+
+ private:
+ string res;
+};
+
+int AmrnbDecTestEnvironment::initFromOptions(int argc, char **argv) {
+ static struct option options[] = {{"res", required_argument, 0, 'P'}, {0, 0, 0, 0}};
+
+ while (true) {
+ int index = 0;
+ int c = getopt_long(argc, argv, "P:", options, &index);
+ if (c == -1) {
+ break;
+ }
+
+ switch (c) {
+ case 'P':
+ setRes(optarg);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (optind < argc) {
+ fprintf(stderr,
+ "unrecognized option: %s\n\n"
+ "usage: %s <gtest options> <test options>\n\n"
+ "test options are:\n\n"
+ "-P, --path: Resource files directory location\n",
+ argv[optind ?: 1], argv[0]);
+ return 2;
+ }
+ return 0;
+}
+
+#endif // __AMRNBDEC_TEST_ENVIRONMENT_H__
diff --git a/media/libstagefright/codecs/amrnb/dec/test/AmrnbDecoderTest.cpp b/media/libstagefright/codecs/amrnb/dec/test/AmrnbDecoderTest.cpp
new file mode 100644
index 0000000..af62074
--- /dev/null
+++ b/media/libstagefright/codecs/amrnb/dec/test/AmrnbDecoderTest.cpp
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "AmrnbDecoderTest"
+#define OUTPUT_FILE "/data/local/tmp/amrnbDecode.out"
+
+#include <utils/Log.h>
+
+#include <audio_utils/sndfile.h>
+#include <stdio.h>
+
+#include "gsmamr_dec.h"
+
+#include "AmrnbDecTestEnvironment.h"
+
+// Constants for AMR-NB
+constexpr int32_t kInputBufferSize = 64;
+constexpr int32_t kSamplesPerFrame = L_FRAME;
+constexpr int32_t kBitsPerSample = 16;
+constexpr int32_t kSampleRate = 8000;
+constexpr int32_t kChannels = 1;
+constexpr int32_t kOutputBufferSize = kSamplesPerFrame * kBitsPerSample / 8;
+const int32_t kFrameSizes[] = {12, 13, 15, 17, 19, 20, 26, 31, -1, -1, -1, -1, -1, -1, -1, -1};
+
+constexpr int32_t kNumFrameReset = 150;
+
+static AmrnbDecTestEnvironment *gEnv = nullptr;
+
+class AmrnbDecoderTest : public ::testing::TestWithParam<string> {
+ public:
+ AmrnbDecoderTest() : mFpInput(nullptr) {}
+
+ ~AmrnbDecoderTest() {
+ if (mFpInput) {
+ fclose(mFpInput);
+ mFpInput = nullptr;
+ }
+ }
+
+ FILE *mFpInput;
+ SNDFILE *openOutputFile(SF_INFO *sfInfo);
+ int32_t DecodeFrames(void *amrHandle, SNDFILE *outFileHandle, int32_t frameCount = INT32_MAX);
+};
+
+SNDFILE *AmrnbDecoderTest::openOutputFile(SF_INFO *sfInfo) {
+ memset(sfInfo, 0, sizeof(SF_INFO));
+ sfInfo->channels = kChannels;
+ sfInfo->format = SF_FORMAT_WAV | SF_FORMAT_PCM_16;
+ sfInfo->samplerate = kSampleRate;
+ SNDFILE *outFileHandle = sf_open(OUTPUT_FILE, SFM_WRITE, sfInfo);
+ return outFileHandle;
+}
+
+int32_t AmrnbDecoderTest::DecodeFrames(void *amrHandle, SNDFILE *outFileHandle,
+ int32_t frameCount) {
+ uint8_t inputBuf[kInputBufferSize];
+ int16_t outputBuf[kOutputBufferSize];
+
+ while (frameCount > 0) {
+ uint8_t mode;
+ int32_t bytesRead = fread(&mode, 1, 1, mFpInput);
+ if (bytesRead != 1) break;
+
+ // Find frame type
+ Frame_Type_3GPP frameType = (Frame_Type_3GPP)((mode >> 3) & 0x0f);
+ int32_t frameSize = kFrameSizes[frameType];
+ if (frameSize < 0) {
+ ALOGE("Illegal frame type");
+ return -1;
+ }
+ bytesRead = fread(inputBuf, 1, frameSize, mFpInput);
+ if (bytesRead != frameSize) break;
+
+ int32_t bytesDecoded = AMRDecode(amrHandle, frameType, inputBuf, outputBuf, MIME_IETF);
+ if (bytesDecoded == -1) {
+ ALOGE("Failed to decode the input file");
+ return -1;
+ }
+
+ sf_writef_short(outFileHandle, outputBuf, kSamplesPerFrame);
+ frameCount--;
+ }
+ return 0;
+}
+
+TEST_F(AmrnbDecoderTest, CreateAmrnbDecoderTest) {
+ void *amrHandle;
+ int32_t status = GSMInitDecode(&amrHandle, (Word8 *)"AMRNBDecoder");
+ ASSERT_EQ(status, 0) << "Error creating AMR-NB decoder";
+ GSMDecodeFrameExit(&amrHandle);
+ ASSERT_EQ(amrHandle, nullptr) << "Error deleting AMR-NB decoder";
+}
+
+TEST_P(AmrnbDecoderTest, DecodeTest) {
+ string inputFile = gEnv->getRes() + GetParam();
+ mFpInput = fopen(inputFile.c_str(), "rb");
+ ASSERT_NE(mFpInput, nullptr) << "Error opening input file " << inputFile;
+
+ // Open the output file.
+ SF_INFO sfInfo;
+ SNDFILE *outFileHandle = openOutputFile(&sfInfo);
+ ASSERT_NE(outFileHandle, nullptr) << "Error opening output file for writing decoded output";
+
+ void *amrHandle;
+ int32_t status = GSMInitDecode(&amrHandle, (Word8 *)"AMRNBDecoder");
+ ASSERT_EQ(status, 0) << "Error creating AMR-NB decoder";
+
+ // Decode
+ int32_t decoderErr = DecodeFrames(amrHandle, outFileHandle);
+ ASSERT_EQ(decoderErr, 0) << "DecodeFrames returned error";
+
+ sf_close(outFileHandle);
+ GSMDecodeFrameExit(&amrHandle);
+ ASSERT_EQ(amrHandle, nullptr) << "Error deleting AMR-NB decoder";
+}
+
+TEST_P(AmrnbDecoderTest, ResetDecodeTest) {
+ string inputFile = gEnv->getRes() + GetParam();
+ mFpInput = fopen(inputFile.c_str(), "rb");
+ ASSERT_NE(mFpInput, nullptr) << "Error opening input file " << inputFile;
+
+ // Open the output file.
+ SF_INFO sfInfo;
+ SNDFILE *outFileHandle = openOutputFile(&sfInfo);
+ ASSERT_NE(outFileHandle, nullptr) << "Error opening output file for writing decoded output";
+
+ void *amrHandle;
+ int32_t status = GSMInitDecode(&amrHandle, (Word8 *)"AMRNBDecoder");
+ ASSERT_EQ(status, 0) << "Error creating AMR-NB decoder";
+
+ // Decode kNumFrameReset first
+ int32_t decoderErr = DecodeFrames(amrHandle, outFileHandle, kNumFrameReset);
+ ASSERT_EQ(decoderErr, 0) << "DecodeFrames returned error";
+
+ status = Speech_Decode_Frame_reset(amrHandle);
+ ASSERT_EQ(status, 0) << "Error resting AMR-NB decoder";
+
+ // Start decoding again
+ decoderErr = DecodeFrames(amrHandle, outFileHandle);
+ ASSERT_EQ(decoderErr, 0) << "DecodeFrames returned error";
+
+ sf_close(outFileHandle);
+ GSMDecodeFrameExit(&amrHandle);
+ ASSERT_EQ(amrHandle, nullptr) << "Error deleting AMR-NB decoder";
+}
+
+INSTANTIATE_TEST_SUITE_P(AmrnbDecoderTestAll, AmrnbDecoderTest,
+ ::testing::Values(("bbb_8000hz_1ch_8kbps_amrnb_30sec.amrnb"),
+ ("sine_amrnb_1ch_12kbps_8000hz.amrnb")));
+
+int main(int argc, char **argv) {
+ gEnv = new AmrnbDecTestEnvironment();
+ ::testing::AddGlobalTestEnvironment(gEnv);
+ ::testing::InitGoogleTest(&argc, argv);
+ int status = gEnv->initFromOptions(argc, argv);
+ if (status == 0) {
+ status = RUN_ALL_TESTS();
+ ALOGV("Test result = %d\n", status);
+ }
+ return status;
+}
diff --git a/media/libstagefright/codecs/amrnb/dec/test/Android.bp b/media/libstagefright/codecs/amrnb/dec/test/Android.bp
new file mode 100644
index 0000000..7a95cfa
--- /dev/null
+++ b/media/libstagefright/codecs/amrnb/dec/test/Android.bp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+cc_test {
+ name: "AmrnbDecoderTest",
+ gtest: true,
+
+ srcs: [
+ "AmrnbDecoderTest.cpp",
+ ],
+
+ static_libs: [
+ "libstagefright_amrnb_common",
+ "libstagefright_amrnbdec",
+ "libaudioutils",
+ "libsndfile",
+ ],
+
+ shared_libs: [
+ "liblog",
+ ],
+
+ cflags: [
+ "-Werror",
+ "-Wall",
+ ],
+
+ sanitize: {
+ cfi: true,
+ misc_undefined: [
+ "unsigned-integer-overflow",
+ "signed-integer-overflow",
+ ],
+ },
+}
diff --git a/media/libstagefright/codecs/amrnb/dec/test/README.md b/media/libstagefright/codecs/amrnb/dec/test/README.md
new file mode 100644
index 0000000..62e13ae
--- /dev/null
+++ b/media/libstagefright/codecs/amrnb/dec/test/README.md
@@ -0,0 +1,34 @@
+## Media Testing ##
+---
+#### AMR-NB Decoder :
+The Amr-Nb Decoder Test Suite validates the amrnb decoder available in libstagefright.
+
+Run the following steps to build the test suite:
+```
+m AmrnbDecoderTest
+```
+
+The 32-bit binaries will be created in the following path : ${OUT}/data/nativetest/
+
+The 64-bit binaries will be created in the following path : ${OUT}/data/nativetest64/
+
+To test 64-bit binary push binaries from nativetest64.
+```
+adb push ${OUT}/data/nativetest64/AmrnbDecoderTest/AmrnbDecoderTest /data/local/tmp/
+```
+
+To test 32-bit binary push binaries from nativetest.
+```
+adb push ${OUT}/data/nativetest/AmrnbDecoderTest/AmrnbDecoderTest /data/local/tmp/
+```
+
+The resource file for the tests is taken from [here](https://drive.google.com/drive/folders/13cM4tAaVFrmr-zGFqaAzFBbKs75pnm9b). Push these files into device for testing.
+Download amr-nb folder and push all the files in this folder to /data/local/tmp/ on the device.
+```
+adb push amr-nb/. /data/local/tmp/
+```
+
+usage: AmrnbDecoderTest -P \<path_to_folder\>
+```
+adb shell /data/local/tmp/AmrnbDecoderTest -P /data/local/tmp/
+```
diff --git a/media/libstagefright/codecs/amrwb/test/AmrwbDecTestEnvironment.h b/media/libstagefright/codecs/amrwb/test/AmrwbDecTestEnvironment.h
new file mode 100644
index 0000000..84d337d
--- /dev/null
+++ b/media/libstagefright/codecs/amrwb/test/AmrwbDecTestEnvironment.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __AMRWBDEC_TEST_ENVIRONMENT_H__
+#define __AMRWBDEC_TEST_ENVIRONMENT_H__
+
+#include <gtest/gtest.h>
+
+#include <getopt.h>
+
+using namespace std;
+
+class AmrwbDecTestEnvironment : public ::testing::Environment {
+ public:
+ AmrwbDecTestEnvironment() : res("/data/local/tmp/") {}
+
+ // Parses the command line arguments
+ int initFromOptions(int argc, char **argv);
+
+ void setRes(const char *_res) { res = _res; }
+
+ const string getRes() const { return res; }
+
+ private:
+ string res;
+};
+
+int AmrwbDecTestEnvironment::initFromOptions(int argc, char **argv) {
+ static struct option options[] = {{"res", required_argument, 0, 'P'}, {0, 0, 0, 0}};
+
+ while (true) {
+ int index = 0;
+ int c = getopt_long(argc, argv, "P:", options, &index);
+ if (c == -1) {
+ break;
+ }
+
+ switch (c) {
+ case 'P':
+ setRes(optarg);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (optind < argc) {
+ fprintf(stderr,
+ "unrecognized option: %s\n\n"
+ "usage: %s <gtest options> <test options>\n\n"
+ "test options are:\n\n"
+ "-P, --path: Resource files directory location\n",
+ argv[optind ?: 1], argv[0]);
+ return 2;
+ }
+ return 0;
+}
+
+#endif // __AMRWBDEC_TEST_ENVIRONMENT_H__
diff --git a/media/libstagefright/codecs/amrwb/test/AmrwbDecoderTest.cpp b/media/libstagefright/codecs/amrwb/test/AmrwbDecoderTest.cpp
new file mode 100644
index 0000000..2aad81b
--- /dev/null
+++ b/media/libstagefright/codecs/amrwb/test/AmrwbDecoderTest.cpp
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "AmrwbDecoderTest"
+#define OUTPUT_FILE "/data/local/tmp/amrwbDecode.out"
+
+#include <utils/Log.h>
+
+#include <audio_utils/sndfile.h>
+#include <stdio.h>
+
+#include "pvamrwbdecoder.h"
+#include "pvamrwbdecoder_api.h"
+
+#include "AmrwbDecTestEnvironment.h"
+
+// Constants for AMR-WB.
+constexpr int32_t kInputBufferSize = 64;
+constexpr int32_t kSamplesPerFrame = 320;
+constexpr int32_t kBitsPerSample = 16;
+constexpr int32_t kSampleRate = 16000;
+constexpr int32_t kChannels = 1;
+constexpr int32_t kMaxSourceDataUnitSize = KAMRWB_NB_BITS_MAX * sizeof(int16_t);
+constexpr int32_t kOutputBufferSize = kSamplesPerFrame * kBitsPerSample / 8;
+const int32_t kFrameSizes[16] = {17, 23, 32, 36, 40, 46, 50, 58, 60, -1, -1, -1, -1, -1, -1, -1};
+constexpr int32_t kNumFrameReset = 150;
+
+constexpr int32_t kMaxCount = 10;
+
+static AmrwbDecTestEnvironment *gEnv = nullptr;
+
+class AmrwbDecoderTest : public ::testing::TestWithParam<string> {
+ public:
+ AmrwbDecoderTest() : mFpInput(nullptr) {}
+
+ ~AmrwbDecoderTest() {
+ if (mFpInput) {
+ fclose(mFpInput);
+ mFpInput = nullptr;
+ }
+ }
+
+ FILE *mFpInput;
+ int32_t DecodeFrames(int16_t *decoderCookie, void *decoderBuf, SNDFILE *outFileHandle,
+ int32_t frameCount = INT32_MAX);
+ SNDFILE *openOutputFile(SF_INFO *sfInfo);
+};
+
+SNDFILE *AmrwbDecoderTest::openOutputFile(SF_INFO *sfInfo) {
+ memset(sfInfo, 0, sizeof(SF_INFO));
+ sfInfo->channels = kChannels;
+ sfInfo->format = SF_FORMAT_WAV | SF_FORMAT_PCM_16;
+ sfInfo->samplerate = kSampleRate;
+ SNDFILE *outFileHandle = sf_open(OUTPUT_FILE, SFM_WRITE, sfInfo);
+ return outFileHandle;
+}
+
+int32_t AmrwbDecoderTest::DecodeFrames(int16_t *decoderCookie, void *decoderBuf,
+ SNDFILE *outFileHandle, int32_t frameCount) {
+ uint8_t inputBuf[kInputBufferSize];
+ int16_t inputSampleBuf[kMaxSourceDataUnitSize];
+ int16_t outputBuf[kOutputBufferSize];
+
+ while (frameCount > 0) {
+ uint8_t modeByte;
+ int32_t bytesRead = fread(&modeByte, 1, 1, mFpInput);
+ if (bytesRead != 1) break;
+
+ int16 mode = ((modeByte >> 3) & 0x0f);
+ if (mode >= 9) {
+ // Produce silence for comfort noise, speech lost and no data.
+ int32_t outputBufferSize = kSamplesPerFrame * kBitsPerSample / 8;
+ memset(outputBuf, 0, outputBufferSize);
+ } else {
+ // Read rest of the frame.
+ int32_t frameSize = kFrameSizes[mode];
+ // AMR-WB file format cannot have mode 10, 11, 12 and 13.
+ if (frameSize < 0) {
+ ALOGE("Illegal frame mode");
+ return -1;
+ }
+ bytesRead = fread(inputBuf, 1, frameSize, mFpInput);
+ if (bytesRead != frameSize) break;
+
+ int16 frameMode = mode;
+ int16 frameType;
+ RX_State_wb rx_state;
+ mime_unsorting(inputBuf, inputSampleBuf, &frameType, &frameMode, 1, &rx_state);
+
+ int16_t numSamplesOutput;
+ pvDecoder_AmrWb(frameMode, inputSampleBuf, outputBuf, &numSamplesOutput, decoderBuf,
+ frameType, decoderCookie);
+ if (numSamplesOutput != kSamplesPerFrame) {
+ ALOGE("Failed to decode the input file");
+ return -1;
+ }
+ for (int count = 0; count < kSamplesPerFrame; ++count) {
+ /* Delete the 2 LSBs (14-bit output) */
+ outputBuf[count] &= 0xfffc;
+ }
+ }
+ sf_writef_short(outFileHandle, outputBuf, kSamplesPerFrame / kChannels);
+ frameCount--;
+ }
+ return 0;
+}
+
+TEST_F(AmrwbDecoderTest, MultiCreateAmrwbDecoderTest) {
+ uint32_t memRequirements = pvDecoder_AmrWbMemRequirements();
+ void *decoderBuf = malloc(memRequirements);
+ ASSERT_NE(decoderBuf, nullptr)
+ << "Failed to allocate decoder memory of size " << memRequirements;
+
+ // Create AMR-WB decoder instance.
+ void *amrHandle = nullptr;
+ int16_t *decoderCookie;
+ for (int count = 0; count < kMaxCount; count++) {
+ pvDecoder_AmrWb_Init(&amrHandle, decoderBuf, &decoderCookie);
+ ASSERT_NE(amrHandle, nullptr) << "Failed to initialize decoder";
+ ALOGV("Decoder created successfully");
+ }
+ if (decoderBuf) {
+ free(decoderBuf);
+ decoderBuf = nullptr;
+ }
+}
+
+TEST_P(AmrwbDecoderTest, DecodeTest) {
+ uint32_t memRequirements = pvDecoder_AmrWbMemRequirements();
+ void *decoderBuf = malloc(memRequirements);
+ ASSERT_NE(decoderBuf, nullptr)
+ << "Failed to allocate decoder memory of size " << memRequirements;
+
+ void *amrHandle = nullptr;
+ int16_t *decoderCookie;
+ pvDecoder_AmrWb_Init(&amrHandle, decoderBuf, &decoderCookie);
+ ASSERT_NE(amrHandle, nullptr) << "Failed to initialize decoder";
+
+ string inputFile = gEnv->getRes() + GetParam();
+ mFpInput = fopen(inputFile.c_str(), "rb");
+ ASSERT_NE(mFpInput, nullptr) << "Error opening input file " << inputFile;
+
+ // Open the output file.
+ SF_INFO sfInfo;
+ SNDFILE *outFileHandle = openOutputFile(&sfInfo);
+ ASSERT_NE(outFileHandle, nullptr) << "Error opening output file for writing decoded output";
+
+ int32_t decoderErr = DecodeFrames(decoderCookie, decoderBuf, outFileHandle);
+ ASSERT_EQ(decoderErr, 0) << "DecodeFrames returned error";
+
+ sf_close(outFileHandle);
+ if (decoderBuf) {
+ free(decoderBuf);
+ decoderBuf = nullptr;
+ }
+}
+
+TEST_P(AmrwbDecoderTest, ResetDecoderTest) {
+ uint32_t memRequirements = pvDecoder_AmrWbMemRequirements();
+ void *decoderBuf = malloc(memRequirements);
+ ASSERT_NE(decoderBuf, nullptr)
+ << "Failed to allocate decoder memory of size " << memRequirements;
+
+ void *amrHandle = nullptr;
+ int16_t *decoderCookie;
+ pvDecoder_AmrWb_Init(&amrHandle, decoderBuf, &decoderCookie);
+ ASSERT_NE(amrHandle, nullptr) << "Failed to initialize decoder";
+
+ string inputFile = gEnv->getRes() + GetParam();
+ mFpInput = fopen(inputFile.c_str(), "rb");
+ ASSERT_NE(mFpInput, nullptr) << "Error opening input file " << inputFile;
+
+ // Open the output file.
+ SF_INFO sfInfo;
+ SNDFILE *outFileHandle = openOutputFile(&sfInfo);
+ ASSERT_NE(outFileHandle, nullptr) << "Error opening output file for writing decoded output";
+
+ // Decode 150 frames first
+ int32_t decoderErr = DecodeFrames(decoderCookie, decoderBuf, outFileHandle, kNumFrameReset);
+ ASSERT_EQ(decoderErr, 0) << "DecodeFrames returned error";
+
+ // Reset Decoder
+ pvDecoder_AmrWb_Reset(decoderBuf, 1);
+
+ // Start decoding again
+ decoderErr = DecodeFrames(decoderCookie, decoderBuf, outFileHandle);
+ ASSERT_EQ(decoderErr, 0) << "DecodeFrames returned error";
+
+ sf_close(outFileHandle);
+ if (decoderBuf) {
+ free(decoderBuf);
+ }
+}
+
+INSTANTIATE_TEST_SUITE_P(AmrwbDecoderTestAll, AmrwbDecoderTest,
+ ::testing::Values(("bbb_amrwb_1ch_14kbps_16000hz.amrwb"),
+ ("bbb_16000hz_1ch_9kbps_amrwb_30sec.amrwb")));
+
+int main(int argc, char **argv) {
+ gEnv = new AmrwbDecTestEnvironment();
+ ::testing::AddGlobalTestEnvironment(gEnv);
+ ::testing::InitGoogleTest(&argc, argv);
+ int status = gEnv->initFromOptions(argc, argv);
+ if (status == 0) {
+ status = RUN_ALL_TESTS();
+ ALOGV("Test result = %d\n", status);
+ }
+ return status;
+}
diff --git a/media/libstagefright/codecs/amrwb/test/Android.bp b/media/libstagefright/codecs/amrwb/test/Android.bp
new file mode 100644
index 0000000..968215a
--- /dev/null
+++ b/media/libstagefright/codecs/amrwb/test/Android.bp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+cc_test {
+ name: "AmrwbDecoderTest",
+ gtest: true,
+
+ srcs: [
+ "AmrwbDecoderTest.cpp",
+ ],
+
+ static_libs: [
+ "libstagefright_amrwbdec",
+ "libsndfile",
+ "libaudioutils",
+ ],
+
+ shared_libs: [
+ "liblog",
+ ],
+
+ cflags: [
+ "-Werror",
+ "-Wall",
+ ],
+
+ sanitize: {
+ cfi: true,
+ misc_undefined: [
+ "unsigned-integer-overflow",
+ "signed-integer-overflow",
+ ],
+ },
+}
diff --git a/media/libstagefright/codecs/amrwb/test/README.md b/media/libstagefright/codecs/amrwb/test/README.md
new file mode 100644
index 0000000..502a20d
--- /dev/null
+++ b/media/libstagefright/codecs/amrwb/test/README.md
@@ -0,0 +1,34 @@
+## Media Testing ##
+---
+#### AMR-WB Decoder :
+The Amr-Wb Decoder Test Suite validates the amrwb decoder available in libstagefright.
+
+Run the following steps to build the test suite:
+```
+m AmrwbDecoderTest
+```
+
+The 32-bit binaries will be created in the following path : ${OUT}/data/nativetest/
+
+The 64-bit binaries will be created in the following path : ${OUT}/data/nativetest64/
+
+To test 64-bit binary push binaries from nativetest64.
+```
+adb push ${OUT}/data/nativetest64/AmrwbDecoderTest/AmrwbDecoderTest /data/local/tmp/
+```
+
+To test 32-bit binary push binaries from nativetest.
+```
+adb push ${OUT}/data/nativetest/AmrwbDecoderTest/AmrwbDecoderTest /data/local/tmp/
+```
+
+The resource file for the tests is taken from [here](https://drive.google.com/drive/folders/13cM4tAaVFrmr-zGFqaAzFBbKs75pnm9b). Push these files into device for testing.
+Download amr-wb folder and push all the files in this folder to /data/local/tmp/ on the device.
+```
+adb push amr-wb/. /data/local/tmp/
+```
+
+usage: AmrwbDecoderTest -P \<path_to_folder\>
+```
+adb shell /data/local/tmp/AmrwbDecoderTest -P /data/local/tmp/
+```
diff --git a/media/libstagefright/id3/test/Android.bp b/media/libstagefright/id3/test/Android.bp
new file mode 100644
index 0000000..9d26eec
--- /dev/null
+++ b/media/libstagefright/id3/test/Android.bp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+cc_test {
+ name: "ID3Test",
+ gtest: true,
+
+ srcs: ["ID3Test.cpp"],
+
+ static_libs: [
+ "libdatasource",
+ "libstagefright_id3",
+ "libstagefright",
+ "libstagefright_foundation",
+ ],
+
+ shared_libs: [
+ "libutils",
+ "liblog",
+ "libbinder",
+ ],
+
+ cflags: [
+ "-Werror",
+ "-Wall",
+ ],
+
+ sanitize: {
+ cfi: true,
+ misc_undefined: [
+ "unsigned-integer-overflow",
+ "signed-integer-overflow",
+ ],
+ },
+}
diff --git a/media/libstagefright/id3/test/ID3Test.cpp b/media/libstagefright/id3/test/ID3Test.cpp
new file mode 100644
index 0000000..a8f1470
--- /dev/null
+++ b/media/libstagefright/id3/test/ID3Test.cpp
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "ID3Test"
+#include <utils/Log.h>
+
+#include <ctype.h>
+#include <string>
+#include <sys/stat.h>
+#include <datasource/FileSource.h>
+
+#include <media/stagefright/foundation/hexdump.h>
+#include <ID3.h>
+
+#include "ID3TestEnvironment.h"
+
+using namespace android;
+
+static ID3TestEnvironment *gEnv = nullptr;
+
+class ID3tagTest : public ::testing::TestWithParam<string> {};
+class ID3versionTest : public ::testing::TestWithParam<pair<string, int>> {};
+class ID3textTagTest : public ::testing::TestWithParam<pair<string, int>> {};
+class ID3albumArtTest : public ::testing::TestWithParam<pair<string, bool>> {};
+class ID3multiAlbumArtTest : public ::testing::TestWithParam<pair<string, int>> {};
+
+TEST_P(ID3tagTest, TagTest) {
+ string path = gEnv->getRes() + GetParam();
+ sp<FileSource> file = new FileSource(path.c_str());
+ ASSERT_EQ(file->initCheck(), (status_t)OK) << "File initialization failed! \n";
+ ID3 tag(file.get());
+ ASSERT_TRUE(tag.isValid()) << "No valid ID3 tag found for " << path.c_str() << "\n";
+
+ ID3::Iterator it(tag, nullptr);
+ while (!it.done()) {
+ String8 id;
+ it.getID(&id);
+ ASSERT_GT(id.length(), 0) << "No ID tag found! \n";
+ ALOGV("Found ID tag: %s\n", String8(id).c_str());
+ it.next();
+ }
+}
+
+TEST_P(ID3versionTest, VersionTest) {
+ int versionNumber = GetParam().second;
+ string path = gEnv->getRes() + GetParam().first;
+ sp<android::FileSource> file = new FileSource(path.c_str());
+ ASSERT_EQ(file->initCheck(), (status_t)OK) << "File initialization failed! \n";
+
+ ID3 tag(file.get());
+ ASSERT_TRUE(tag.isValid()) << "No valid ID3 tag found for " << path.c_str() << "\n";
+ ASSERT_TRUE(tag.version() >= versionNumber)
+ << "Expected version: " << tag.version() << " Found version: " << versionNumber;
+}
+
+TEST_P(ID3textTagTest, TextTagTest) {
+ int numTextFrames = GetParam().second;
+ string path = gEnv->getRes() + GetParam().first;
+ sp<android::FileSource> file = new FileSource(path.c_str());
+ ASSERT_EQ(file->initCheck(), (status_t)OK) << "File initialization failed! \n";
+
+ ID3 tag(file.get());
+ ASSERT_TRUE(tag.isValid()) << "No valid ID3 tag found for " << path.c_str() << "\n";
+ int countTextFrames = 0;
+ ID3::Iterator it(tag, nullptr);
+ while (!it.done()) {
+ String8 id;
+ it.getID(&id);
+ ASSERT_GT(id.length(), 0);
+ if (id[0] == 'T') {
+ String8 text;
+ countTextFrames++;
+ it.getString(&text);
+ ALOGV("Found text frame %s : %s \n", id.string(), text.string());
+ }
+ it.next();
+ }
+ ASSERT_EQ(countTextFrames, numTextFrames)
+ << "Expected " << numTextFrames << " text frames, found " << countTextFrames;
+}
+
+TEST_P(ID3albumArtTest, AlbumArtTest) {
+ bool albumArtPresent = GetParam().second;
+ string path = gEnv->getRes() + GetParam().first;
+ sp<android::FileSource> file = new FileSource(path.c_str());
+ ASSERT_EQ(file->initCheck(), (status_t)OK) << "File initialization failed! \n";
+
+ ID3 tag(file.get());
+ ASSERT_TRUE(tag.isValid()) << "No valid ID3 tag found for " << path.c_str() << "\n";
+ size_t dataSize;
+ String8 mime;
+ const void *data = tag.getAlbumArt(&dataSize, &mime);
+
+ if (albumArtPresent) {
+ if (data) {
+ ALOGV("Found album art: size = %zu mime = %s \n", dataSize, mime.string());
+ }
+ ASSERT_NE(data, nullptr) << "Expected album art, found none!" << path;
+ } else {
+ ASSERT_EQ(data, nullptr) << "Found album art when expected none!";
+ }
+#if (LOG_NDEBUG == 0)
+ hexdump(data, dataSize > 128 ? 128 : dataSize);
+#endif
+}
+
+TEST_P(ID3multiAlbumArtTest, MultiAlbumArtTest) {
+ int numAlbumArt = GetParam().second;
+ string path = gEnv->getRes() + GetParam().first;
+ sp<android::FileSource> file = new FileSource(path.c_str());
+ ASSERT_EQ(file->initCheck(), (status_t)OK) << "File initialization failed! \n";
+
+ ID3 tag(file.get());
+ ASSERT_TRUE(tag.isValid()) << "No valid ID3 tag found for " << path.c_str() << "\n";
+ int count = 0;
+ ID3::Iterator it(tag, nullptr);
+ while (!it.done()) {
+ String8 id;
+ it.getID(&id);
+ ASSERT_GT(id.length(), 0);
+ // Check if the tag is an "APIC/PIC" tag.
+ if (String8(id) == "APIC" || String8(id) == "PIC") {
+ count++;
+ size_t dataSize;
+ String8 mime;
+ const void *data = tag.getAlbumArt(&dataSize, &mime);
+ if (data) {
+ ALOGV("Found album art: size = %zu mime = %s \n", dataSize, mime.string());
+#if (LOG_NDEBUG == 0)
+ hexdump(data, dataSize > 128 ? 128 : dataSize);
+#endif
+ }
+ ASSERT_NE(data, nullptr) << "Expected album art, found none!" << path;
+ }
+ it.next();
+ }
+ ASSERT_EQ(count, numAlbumArt) << "Found " << count << " album arts, expected " << numAlbumArt
+ << " album arts! \n";
+}
+
+INSTANTIATE_TEST_SUITE_P(id3TestAll, ID3tagTest,
+ ::testing::Values("bbb_44100hz_2ch_128kbps_mp3_30sec.mp3",
+ "bbb_44100hz_2ch_128kbps_mp3_30sec_1_image.mp3",
+ "bbb_44100hz_2ch_128kbps_mp3_30sec_2_image.mp3",
+ "bbb_44100hz_2ch_128kbps_mp3_5mins.mp3",
+ "bbb_44100hz_2ch_128kbps_mp3_5mins_1_image.mp3",
+ "bbb_44100hz_2ch_128kbps_mp3_5mins_2_image.mp3",
+ "bbb_44100hz_2ch_128kbps_mp3_5mins_largeSize.mp3",
+ "bbb_44100hz_2ch_128kbps_mp3_30sec_moreTextFrames.mp3"));
+
+INSTANTIATE_TEST_SUITE_P(
+ id3TestAll, ID3versionTest,
+ ::testing::Values(make_pair("bbb_44100hz_2ch_128kbps_mp3_30sec.mp3", 4),
+ make_pair("bbb_44100hz_2ch_128kbps_mp3_30sec_1_image.mp3", 4),
+ make_pair("bbb_44100hz_2ch_128kbps_mp3_30sec_2_image.mp3", 4),
+ make_pair("bbb_44100hz_2ch_128kbps_mp3_5mins.mp3", 4),
+ make_pair("bbb_44100hz_2ch_128kbps_mp3_5mins_1_image.mp3", 4),
+ make_pair("bbb_44100hz_2ch_128kbps_mp3_5mins_2_image.mp3", 4),
+ make_pair("bbb_44100hz_2ch_128kbps_mp3_5mins_largeSize.mp3", 4),
+ make_pair("bbb_44100hz_2ch_128kbps_mp3_30sec_moreTextFrames.mp3", 4)));
+
+INSTANTIATE_TEST_SUITE_P(
+ id3TestAll, ID3textTagTest,
+ ::testing::Values(make_pair("bbb_44100hz_2ch_128kbps_mp3_30sec.mp3", 1),
+ make_pair("bbb_44100hz_2ch_128kbps_mp3_30sec_1_image.mp3", 1),
+ make_pair("bbb_44100hz_2ch_128kbps_mp3_30sec_2_image.mp3", 1),
+ make_pair("bbb_44100hz_2ch_128kbps_mp3_5mins.mp3", 1),
+ make_pair("bbb_44100hz_2ch_128kbps_mp3_5mins_1_image.mp3", 1),
+ make_pair("bbb_44100hz_2ch_128kbps_mp3_5mins_2_image.mp3", 1),
+ make_pair("bbb_44100hz_2ch_128kbps_mp3_5mins_largeSize.mp3", 1),
+ make_pair("bbb_44100hz_2ch_128kbps_mp3_30sec_moreTextFrames.mp3", 5)));
+
+INSTANTIATE_TEST_SUITE_P(
+ id3TestAll, ID3albumArtTest,
+ ::testing::Values(make_pair("bbb_44100hz_2ch_128kbps_mp3_30sec.mp3", false),
+ make_pair("bbb_44100hz_2ch_128kbps_mp3_30sec_1_image.mp3", true),
+ make_pair("bbb_44100hz_2ch_128kbps_mp3_30sec_2_image.mp3", true),
+ make_pair("bbb_44100hz_2ch_128kbps_mp3_5mins.mp3", false),
+ make_pair("bbb_44100hz_2ch_128kbps_mp3_5mins_1_image.mp3", true),
+ make_pair("bbb_44100hz_2ch_128kbps_mp3_5mins_2_image.mp3", true),
+ make_pair("bbb_44100hz_2ch_128kbps_mp3_5mins_largeSize.mp3", true)));
+
+INSTANTIATE_TEST_SUITE_P(
+ id3TestAll, ID3multiAlbumArtTest,
+ ::testing::Values(make_pair("bbb_44100hz_2ch_128kbps_mp3_30sec.mp3", 0),
+ make_pair("bbb_44100hz_2ch_128kbps_mp3_5mins.mp3", 0),
+ make_pair("bbb_44100hz_2ch_128kbps_mp3_30sec_1_image.mp3", 1),
+ make_pair("bbb_44100hz_2ch_128kbps_mp3_5mins_1_image.mp3", 1),
+ make_pair("bbb_44100hz_2ch_128kbps_mp3_30sec_2_image.mp3", 2),
+ make_pair("bbb_44100hz_2ch_128kbps_mp3_5mins_2_image.mp3", 2),
+ make_pair("bbb_44100hz_2ch_128kbps_mp3_5mins_largeSize.mp3", 3)));
+
+int main(int argc, char **argv) {
+ gEnv = new ID3TestEnvironment();
+ ::testing::AddGlobalTestEnvironment(gEnv);
+ ::testing::InitGoogleTest(&argc, argv);
+ int status = gEnv->initFromOptions(argc, argv);
+ if (status == 0) {
+ status = RUN_ALL_TESTS();
+ ALOGI("ID3 Test result = %d\n", status);
+ }
+ return status;
+}
diff --git a/media/libstagefright/id3/test/ID3TestEnvironment.h b/media/libstagefright/id3/test/ID3TestEnvironment.h
new file mode 100644
index 0000000..2229718
--- /dev/null
+++ b/media/libstagefright/id3/test/ID3TestEnvironment.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __ID3_TEST_ENVIRONMENT_H__
+#define __ID3_TEST_ENVIRONMENT_H__
+
+#include <gtest/gtest.h>
+
+#include <getopt.h>
+
+using namespace std;
+
+class ID3TestEnvironment : public::testing::Environment {
+ public:
+ ID3TestEnvironment() : res("/data/local/tmp/") {}
+
+ // Parses the command line arguments
+ int initFromOptions(int argc, char **argv);
+
+ void setRes(const char *_res) { res = _res; }
+
+ const string getRes() const { return res; }
+
+ private:
+ string res;
+};
+
+int ID3TestEnvironment::initFromOptions(int argc, char **argv) {
+ static struct option options[] = {{"path", required_argument, 0, 'P'}, {0, 0, 0, 0}};
+
+ while (true) {
+ int index = 0;
+ int c = getopt_long(argc, argv, "P:", options, &index);
+ if (c == -1) {
+ break;
+ }
+
+ switch (c) {
+ case 'P': {
+ setRes(optarg);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ if (optind < argc) {
+ fprintf(stderr,
+ "unrecognized option: %s\n\n"
+ "usage: %s <gtest options> <test options>\n\n"
+ "test options are:\n\n"
+ "-P, --path: Resource files directory location\n",
+ argv[optind ?: 1], argv[0]);
+ return 2;
+ }
+ return 0;
+}
+
+#endif // __ID3_TEST_ENVIRONMENT_H__
diff --git a/media/libstagefright/id3/test/README.md b/media/libstagefright/id3/test/README.md
new file mode 100644
index 0000000..72f730c
--- /dev/null
+++ b/media/libstagefright/id3/test/README.md
@@ -0,0 +1,34 @@
+## Media Testing ##
+---
+#### ID3 Test :
+The ID3 Test Suite validates the ID3 parser available in libstagefright.
+
+Run the following command in the id3 folder to build the test suite:
+```
+m ID3Test
+```
+
+The 32-bit binaries will be created in the following path : ${OUT}/data/nativetest/
+
+The 64-bit binaries will be created in the following path : ${OUT}/data/nativetest64/
+
+To test 64-bit binary push binaries from nativetest64.
+```
+adb push ${OUT}/data/nativetest64/ID3Test/ID3Test /data/local/tmp/
+```
+
+To test 32-bit binary push binaries from nativetest.
+```
+adb push ${OUT}/data/nativetest/ID3Test/ID3Test /data/local/tmp/
+```
+
+The resource file for the tests is taken from [here](https://drive.google.com/drive/folders/1pt5HFVSysbqfyqY1sVJ9MTupZKCdqjYZ). Push these files into device for testing.
+Download ID3 folder and push all the files in this folder to /data/local/tmp/ID3 on the device.
+```
+adb push ID3/. /data/local/tmp/ID3
+```
+
+usage: ID3Test -P \<path_to_folder\>
+```
+adb shell /data/local/tmp/ID3Test -P /data/local/tmp/ID3/
+```
diff --git a/media/libstagefright/include/media/stagefright/MPEG4Writer.h b/media/libstagefright/include/media/stagefright/MPEG4Writer.h
index 0564835..34a7d55 100644
--- a/media/libstagefright/include/media/stagefright/MPEG4Writer.h
+++ b/media/libstagefright/include/media/stagefright/MPEG4Writer.h
@@ -23,6 +23,7 @@
#include <media/stagefright/MediaWriter.h>
#include <utils/List.h>
#include <utils/threads.h>
+#include <map>
#include <media/stagefright/foundation/AHandlerReflector.h>
#include <media/stagefright/foundation/ALooper.h>
#include <mutex>
@@ -220,8 +221,9 @@
uint32_t mPrimaryItemId;
uint32_t mAssociationEntryCount;
uint32_t mNumGrids;
+ uint16_t mNextItemId;
bool mHasRefs;
- Vector<ItemInfo> mItems;
+ std::map<uint32_t, ItemInfo> mItems;
Vector<ItemProperty> mProperties;
// Writer thread handling
@@ -277,6 +279,7 @@
void addLengthPrefixedSample_l(MediaBuffer *buffer);
void addMultipleLengthPrefixedSamples_l(MediaBuffer *buffer);
uint16_t addProperty_l(const ItemProperty &);
+ status_t reserveItemId_l(size_t numItems, uint16_t *itemIdBase);
uint16_t addItem_l(const ItemInfo &);
void addRefs_l(uint16_t itemId, const ItemRefs &);
diff --git a/media/tests/benchmark/MediaBenchmarkTest/Android.bp b/media/tests/benchmark/MediaBenchmarkTest/Android.bp
index 91b03f1..489ab04 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/Android.bp
+++ b/media/tests/benchmark/MediaBenchmarkTest/Android.bp
@@ -29,6 +29,10 @@
"android.test.base",
],
+ jni_libs: [
+ "libmediabenchmark_jni",
+ ],
+
static_libs: [
"libMediaBenchmark",
"junit",
diff --git a/media/tests/benchmark/MediaBenchmarkTest/build.gradle b/media/tests/benchmark/MediaBenchmarkTest/build.gradle
index b0ee692..b2aee1a 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/build.gradle
+++ b/media/tests/benchmark/MediaBenchmarkTest/build.gradle
@@ -30,7 +30,7 @@
compileSdkVersion 29
defaultConfig {
applicationId "com.android.media.benchmark"
- minSdkVersion 21
+ minSdkVersion 28
targetSdkVersion 29
versionCode 1
versionName "1.0"
@@ -48,6 +48,18 @@
manifest.srcFile 'AndroidManifest.xml'
}
}
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+ externalNativeBuild {
+ cmake {
+ path "src/main/cpp/CMakeLists.txt"
+ version "3.10.2"
+ }
+ }
}
repositories {
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/DecoderTest.java b/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/DecoderTest.java
index be2633d..07d414d 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/DecoderTest.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/DecoderTest.java
@@ -27,7 +27,10 @@
import com.android.media.benchmark.library.CodecUtils;
import com.android.media.benchmark.library.Decoder;
import com.android.media.benchmark.library.Extractor;
+import com.android.media.benchmark.library.Native;
+import com.android.media.benchmark.library.Stats;
+import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -44,12 +47,17 @@
import java.util.Arrays;
import java.util.Collection;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
+
@RunWith(Parameterized.class)
public class DecoderTest {
private static final Context mContext =
InstrumentationRegistry.getInstrumentation().getTargetContext();
private static final String mInputFilePath = mContext.getString(R.string.input_file_path);
private static final String mOutputFilePath = mContext.getString(R.string.output_file_path);
+ private static final String mStatsFile =
+ mContext.getFilesDir() + "/Decoder." + System.currentTimeMillis() + ".csv";
private static final String TAG = "DecoderTest";
private static final long PER_TEST_TIMEOUT_MS = 60000;
private static final boolean DEBUG = false;
@@ -101,97 +109,111 @@
{"crowd_1920x1080_25fps_4000kbps_h265.mkv", true}});
}
+ @BeforeClass
+ public static void writeStatsHeaderToFile() throws IOException {
+ Stats mStats = new Stats();
+ boolean status = mStats.writeStatsHeader(mStatsFile);
+ assertTrue("Unable to open stats file for writing!", status);
+ }
+
@Test(timeout = PER_TEST_TIMEOUT_MS)
public void testDecoder() throws IOException {
File inputFile = new File(mInputFilePath + mInputFile);
- if (inputFile.exists()) {
- FileInputStream fileInput = new FileInputStream(inputFile);
- FileDescriptor fileDescriptor = fileInput.getFD();
- Extractor extractor = new Extractor();
- int trackCount = extractor.setUpExtractor(fileDescriptor);
- ArrayList<ByteBuffer> inputBuffer = new ArrayList<>();
- ArrayList<MediaCodec.BufferInfo> frameInfo = new ArrayList<>();
- if (trackCount <= 0) {
- Log.e(TAG, "Extraction failed. No tracks for file: " + mInputFile);
- return;
- }
- for (int currentTrack = 0; currentTrack < trackCount; currentTrack++) {
- extractor.selectExtractorTrack(currentTrack);
- MediaFormat format = extractor.getFormat(currentTrack);
- String mime = format.getString(MediaFormat.KEY_MIME);
- ArrayList<String> mediaCodecs = CodecUtils.selectCodecs(mime, false);
- if (mediaCodecs.size() <= 0) {
- Log.e(TAG,
- "No suitable codecs found for file: " + mInputFile
- + " track : " + currentTrack + " mime: " + mime);
- continue;
+ assertTrue("Cannot find " + mInputFile + " in directory " + mInputFilePath,
+ inputFile.exists());
+ FileInputStream fileInput = new FileInputStream(inputFile);
+ FileDescriptor fileDescriptor = fileInput.getFD();
+ Extractor extractor = new Extractor();
+ int trackCount = extractor.setUpExtractor(fileDescriptor);
+ assertTrue("Extraction failed. No tracks for file: " + mInputFile, (trackCount > 0));
+ ArrayList<ByteBuffer> inputBuffer = new ArrayList<>();
+ ArrayList<MediaCodec.BufferInfo> frameInfo = new ArrayList<>();
+ for (int currentTrack = 0; currentTrack < trackCount; currentTrack++) {
+ extractor.selectExtractorTrack(currentTrack);
+ MediaFormat format = extractor.getFormat(currentTrack);
+ String mime = format.getString(MediaFormat.KEY_MIME);
+ ArrayList<String> mediaCodecs = CodecUtils.selectCodecs(mime, false);
+ assertTrue("No suitable codecs found for file: " + mInputFile + " track : " +
+ currentTrack + " mime: " + mime, (mediaCodecs.size() > 0));
+
+ // Get samples from extractor
+ int sampleSize;
+ do {
+ sampleSize = extractor.getFrameSample();
+ MediaCodec.BufferInfo bufInfo = new MediaCodec.BufferInfo();
+ MediaCodec.BufferInfo info = extractor.getBufferInfo();
+ ByteBuffer dataBuffer = ByteBuffer.allocate(info.size);
+ dataBuffer.put(extractor.getFrameBuffer().array(), 0, info.size);
+ bufInfo.set(info.offset, info.size, info.presentationTimeUs, info.flags);
+ inputBuffer.add(dataBuffer);
+ frameInfo.add(bufInfo);
+ if (DEBUG) {
+ Log.d(TAG, "Extracted bufInfo: flag = " + bufInfo.flags + " timestamp = " +
+ bufInfo.presentationTimeUs + " size = " + bufInfo.size);
}
- // Get samples from extractor
- int sampleSize;
- do {
- sampleSize = extractor.getFrameSample();
- MediaCodec.BufferInfo bufInfo = new MediaCodec.BufferInfo();
- MediaCodec.BufferInfo info = extractor.getBufferInfo();
- ByteBuffer dataBuffer = ByteBuffer.allocate(info.size);
- dataBuffer.put(extractor.getFrameBuffer().array(), 0, info.size);
- bufInfo.set(info.offset, info.size, info.presentationTimeUs, info.flags);
- inputBuffer.add(dataBuffer);
- frameInfo.add(bufInfo);
- if (DEBUG) {
- Log.d(TAG,
- "Extracted bufInfo: flag = " + bufInfo.flags + " timestamp = "
- + bufInfo.presentationTimeUs + " size = " + bufInfo.size);
+ } while (sampleSize > 0);
+ for (String codecName : mediaCodecs) {
+ FileOutputStream decodeOutputStream = null;
+ if (WRITE_OUTPUT) {
+ if (!Paths.get(mOutputFilePath).toFile().exists()) {
+ Files.createDirectories(Paths.get(mOutputFilePath));
}
- } while (sampleSize > 0);
- for (String codecName : mediaCodecs) {
- FileOutputStream decodeOutputStream = null;
- if (WRITE_OUTPUT) {
- if (!Paths.get(mOutputFilePath).toFile().exists()) {
- Files.createDirectories(Paths.get(mOutputFilePath));
- }
- File outFile = new File(mOutputFilePath + "decoder.out");
- if (outFile.exists()) {
- if (!outFile.delete()) {
- Log.e(TAG, " Unable to delete existing file" + outFile.toString());
- }
- }
- if (outFile.createNewFile()) {
- decodeOutputStream = new FileOutputStream(outFile);
- } else {
- Log.e(TAG, "Unable to create file: " + outFile.toString());
- }
+ File outFile = new File(mOutputFilePath + "decoder.out");
+ if (outFile.exists()) {
+ assertTrue(" Unable to delete existing file" + outFile.toString(),
+ outFile.delete());
}
- Decoder decoder = new Decoder();
- decoder.setupDecoder(decodeOutputStream);
- int status =
- decoder.decode(inputBuffer, frameInfo, mAsyncMode, format, codecName);
- decoder.deInitCodec();
- if (status == 0) {
- decoder.dumpStatistics(
- mInputFile + " " + codecName, extractor.getClipDuration());
- Log.i(TAG,
- "Decoding Successful for file: " + mInputFile
- + " with codec: " + codecName);
- } else {
- Log.e(TAG,
- "Decoder returned error " + status + " for file: " + mInputFile
- + " with codec: " + codecName);
- }
- decoder.resetDecoder();
- if (decodeOutputStream != null) {
- decodeOutputStream.close();
- }
+ assertTrue("Unable to create file: " + outFile.toString(),
+ outFile.createNewFile());
+ decodeOutputStream = new FileOutputStream(outFile);
}
- extractor.unselectExtractorTrack(currentTrack);
- inputBuffer.clear();
- frameInfo.clear();
+ Decoder decoder = new Decoder();
+ decoder.setupDecoder(decodeOutputStream);
+ int status = decoder.decode(inputBuffer, frameInfo, mAsyncMode, format, codecName);
+ decoder.deInitCodec();
+ assertEquals("Decoder returned error " + status + " for file: " + mInputFile +
+ " with codec: " + codecName, 0, status);
+ decoder.dumpStatistics(mInputFile, codecName, (mAsyncMode ? "async" : "sync"),
+ extractor.getClipDuration(), mStatsFile);
+ Log.i(TAG, "Decoding Successful for file: " + mInputFile + " with codec: " +
+ codecName);
+ decoder.resetDecoder();
+ if (decodeOutputStream != null) {
+ decodeOutputStream.close();
+ }
}
- extractor.deinitExtractor();
- fileInput.close();
- } else {
- Log.w(TAG,
- "Warning: Test Skipped. Cannot find " + mInputFile + " in directory "
- + mInputFilePath);
+ extractor.unselectExtractorTrack(currentTrack);
+ inputBuffer.clear();
+ frameInfo.clear();
}
+ extractor.deinitExtractor();
+ fileInput.close();
+ }
+
+ @Test
+ public void testNativeDecoder() throws IOException {
+ File inputFile = new File(mInputFilePath + mInputFile);
+ assertTrue("Cannot find " + mInputFile + " in directory " + mInputFilePath,
+ inputFile.exists());
+ FileInputStream fileInput = new FileInputStream(inputFile);
+ FileDescriptor fileDescriptor = fileInput.getFD();
+ Extractor extractor = new Extractor();
+ int trackCount = extractor.setUpExtractor(fileDescriptor);
+ assertTrue("Extraction failed. No tracks for file: ", trackCount > 0);
+ for (int currentTrack = 0; currentTrack < trackCount; currentTrack++) {
+ extractor.selectExtractorTrack(currentTrack);
+ MediaFormat format = extractor.getFormat(currentTrack);
+ String mime = format.getString(MediaFormat.KEY_MIME);
+ ArrayList<String> mediaCodecs = CodecUtils.selectCodecs(mime, false);
+ for (String codecName : mediaCodecs) {
+ Log.i("Test: %s\n", mInputFile);
+ Native nativeDecoder = new Native();
+ int status = nativeDecoder.Decode(
+ mInputFilePath, mInputFile, mStatsFile, codecName, mAsyncMode);
+ assertEquals("Decoder returned error " + status + " for file: " + mInputFile, 0,
+ status);
+ }
+ }
+ fileInput.close();
}
}
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/EncoderTest.java b/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/EncoderTest.java
index 9db9c84..00e5e21 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/EncoderTest.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/EncoderTest.java
@@ -28,7 +28,10 @@
import com.android.media.benchmark.library.Decoder;
import com.android.media.benchmark.library.Encoder;
import com.android.media.benchmark.library.Extractor;
+import com.android.media.benchmark.library.Native;
+import com.android.media.benchmark.library.Stats;
+import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -37,18 +40,24 @@
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
+import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
+
@RunWith(Parameterized.class)
public class EncoderTest {
private static final Context mContext =
InstrumentationRegistry.getInstrumentation().getTargetContext();
private static final String mInputFilePath = mContext.getString(R.string.input_file_path);
private static final String mOutputFilePath = mContext.getString(R.string.output_file_path);
+ private static final String mStatsFile =
+ mContext.getFilesDir() + "/Encoder." + System.currentTimeMillis() + ".csv";
private static final String TAG = "EncoderTest";
private static final long PER_TEST_TIMEOUT_MS = 120000;
private static final boolean DEBUG = false;
@@ -56,7 +65,6 @@
private static final int ENCODE_DEFAULT_FRAME_RATE = 25;
private static final int ENCODE_DEFAULT_BIT_RATE = 8000000 /* 8 Mbps */;
private static final int ENCODE_MIN_BIT_RATE = 600000 /* 600 Kbps */;
-
private String mInputFile;
@Parameterized.Parameters
@@ -81,192 +89,204 @@
this.mInputFile = inputFileName;
}
+ @BeforeClass
+ public static void writeStatsHeaderToFile() throws IOException {
+ Stats mStats = new Stats();
+ boolean status = mStats.writeStatsHeader(mStatsFile);
+ assertTrue("Unable to open stats file for writing!", status);
+ }
+
@Test(timeout = PER_TEST_TIMEOUT_MS)
public void sampleEncoderTest() throws Exception {
int status;
int frameSize;
-
//Parameters for video
int width = 0;
int height = 0;
int profile = 0;
int level = 0;
int frameRate = 0;
-
//Parameters for audio
int bitRate = 0;
int sampleRate = 0;
int numChannels = 0;
-
File inputFile = new File(mInputFilePath + mInputFile);
- if (inputFile.exists()) {
- FileInputStream fileInput = new FileInputStream(inputFile);
- FileDescriptor fileDescriptor = fileInput.getFD();
- Extractor extractor = new Extractor();
- int trackCount = extractor.setUpExtractor(fileDescriptor);
- if (trackCount <= 0) {
- Log.e(TAG, "Extraction failed. No tracks for file: " + mInputFile);
- return;
+ assertTrue("Cannot find " + mInputFile + " in directory " + mInputFilePath,
+ inputFile.exists());
+ FileInputStream fileInput = new FileInputStream(inputFile);
+ FileDescriptor fileDescriptor = fileInput.getFD();
+ Extractor extractor = new Extractor();
+ int trackCount = extractor.setUpExtractor(fileDescriptor);
+ assertTrue("Extraction failed. No tracks for file: " + mInputFile, (trackCount > 0));
+ ArrayList<ByteBuffer> inputBuffer = new ArrayList<>();
+ ArrayList<MediaCodec.BufferInfo> frameInfo = new ArrayList<>();
+ for (int currentTrack = 0; currentTrack < trackCount; currentTrack++) {
+ extractor.selectExtractorTrack(currentTrack);
+ MediaFormat format = extractor.getFormat(currentTrack);
+ // Get samples from extractor
+ int sampleSize;
+ do {
+ sampleSize = extractor.getFrameSample();
+ MediaCodec.BufferInfo bufInfo = new MediaCodec.BufferInfo();
+ MediaCodec.BufferInfo info = extractor.getBufferInfo();
+ ByteBuffer dataBuffer = ByteBuffer.allocate(info.size);
+ dataBuffer.put(extractor.getFrameBuffer().array(), 0, info.size);
+ bufInfo.set(info.offset, info.size, info.presentationTimeUs, info.flags);
+ inputBuffer.add(dataBuffer);
+ frameInfo.add(bufInfo);
+ if (DEBUG) {
+ Log.d(TAG, "Extracted bufInfo: flag = " + bufInfo.flags + " timestamp = " +
+ bufInfo.presentationTimeUs + " size = " + bufInfo.size);
+ }
+ } while (sampleSize > 0);
+ int tid = android.os.Process.myTid();
+ File decodedFile = new File(mContext.getFilesDir() + "/decoder_" + tid + ".out");
+ FileOutputStream decodeOutputStream = new FileOutputStream(decodedFile);
+ Decoder decoder = new Decoder();
+ decoder.setupDecoder(decodeOutputStream);
+ status = decoder.decode(inputBuffer, frameInfo, false, format, "");
+ assertEquals("Decoder returned error " + status + " for file: " + mInputFile, 0,
+ status);
+ decoder.deInitCodec();
+ extractor.unselectExtractorTrack(currentTrack);
+ inputBuffer.clear();
+ frameInfo.clear();
+ if (decodeOutputStream != null) {
+ decodeOutputStream.close();
}
- ArrayList<ByteBuffer> inputBuffer = new ArrayList<>();
- ArrayList<MediaCodec.BufferInfo> frameInfo = new ArrayList<>();
- for (int currentTrack = 0; currentTrack < trackCount; currentTrack++) {
- extractor.selectExtractorTrack(currentTrack);
- MediaFormat format = extractor.getFormat(currentTrack);
- // Get samples from extractor
- int sampleSize;
- do {
- sampleSize = extractor.getFrameSample();
- MediaCodec.BufferInfo bufInfo = new MediaCodec.BufferInfo();
- MediaCodec.BufferInfo info = extractor.getBufferInfo();
- ByteBuffer dataBuffer = ByteBuffer.allocate(info.size);
- dataBuffer.put(extractor.getFrameBuffer().array(), 0, info.size);
- bufInfo.set(info.offset, info.size, info.presentationTimeUs, info.flags);
- inputBuffer.add(dataBuffer);
- frameInfo.add(bufInfo);
+ String mime = format.getString(MediaFormat.KEY_MIME);
+ ArrayList<String> mediaCodecs = CodecUtils.selectCodecs(mime, true);
+ assertTrue("No suitable codecs found for file: " + mInputFile + " track : " +
+ currentTrack + " mime: " + mime, (mediaCodecs.size() > 0));
+ Boolean[] encodeMode = {true, false};
+ /* Encoding the decoder's output */
+ for (Boolean asyncMode : encodeMode) {
+ for (String codecName : mediaCodecs) {
+ FileOutputStream encodeOutputStream = null;
+ if (WRITE_OUTPUT) {
+ File outEncodeFile = new File(mOutputFilePath + "encoder.out");
+ if (outEncodeFile.exists()) {
+ assertTrue(" Unable to delete existing file" + outEncodeFile.toString(),
+ outEncodeFile.delete());
+ }
+ assertTrue("Unable to create file to write encoder output: " +
+ outEncodeFile.toString(), outEncodeFile.createNewFile());
+ encodeOutputStream = new FileOutputStream(outEncodeFile);
+ }
+ File rawFile = new File(mContext.getFilesDir() + "/decoder_" + tid + ".out");
+ assertTrue("Cannot open file to write decoded output", rawFile.exists());
if (DEBUG) {
- Log.d(TAG, "Extracted bufInfo: flag = " + bufInfo.flags + " timestamp = " +
- bufInfo.presentationTimeUs + " size = " + bufInfo.size);
+ Log.i(TAG, "Path of decoded input file: " + rawFile.toString());
}
- } while (sampleSize > 0);
-
- int tid = android.os.Process.myTid();
- File decodedFile = new File(mContext.getFilesDir() + "/decoder_" + tid + ".out");
- FileOutputStream decodeOutputStream = new FileOutputStream(decodedFile);
- Decoder decoder = new Decoder();
- decoder.setupDecoder(decodeOutputStream);
- status = decoder.decode(inputBuffer, frameInfo, false, format, "");
- if (status == 0) {
- Log.i(TAG, "Decoding complete.");
- } else {
- Log.e(TAG, "Decode returned error. Encoding did not take place." + status);
- return;
- }
- decoder.deInitCodec();
- extractor.unselectExtractorTrack(currentTrack);
- inputBuffer.clear();
- frameInfo.clear();
- if (decodeOutputStream != null) {
- decodeOutputStream.close();
- }
- String mime = format.getString(MediaFormat.KEY_MIME);
- ArrayList<String> mediaCodecs = CodecUtils.selectCodecs(mime, true);
- if (mediaCodecs.size() <= 0) {
- Log.e(TAG, "No suitable codecs found for file: " + mInputFile + " track : " +
- currentTrack + " mime: " + mime);
- return;
- }
- Boolean[] encodeMode = {true, false};
- /* Encoding the decoder's output */
- for (Boolean asyncMode : encodeMode) {
- for (String codecName : mediaCodecs) {
- FileOutputStream encodeOutputStream = null;
- if (WRITE_OUTPUT) {
- File outEncodeFile = new File(mOutputFilePath + "encoder.out");
- if (outEncodeFile.exists()) {
- if (!outEncodeFile.delete()) {
- Log.e(TAG, "Unable to delete existing file" +
- decodedFile.toString());
- }
- }
- if (outEncodeFile.createNewFile()) {
- encodeOutputStream = new FileOutputStream(outEncodeFile);
+ FileInputStream eleStream = new FileInputStream(rawFile);
+ if (mime.startsWith("video/")) {
+ width = format.getInteger(MediaFormat.KEY_WIDTH);
+ height = format.getInteger(MediaFormat.KEY_HEIGHT);
+ if (format.containsKey(MediaFormat.KEY_FRAME_RATE)) {
+ frameRate = format.getInteger(MediaFormat.KEY_FRAME_RATE);
+ } else if (frameRate <= 0) {
+ frameRate = ENCODE_DEFAULT_FRAME_RATE;
+ }
+ if (format.containsKey(MediaFormat.KEY_BIT_RATE)) {
+ bitRate = format.getInteger(MediaFormat.KEY_BIT_RATE);
+ } else if (bitRate <= 0) {
+ if (mime.contains("video/3gpp") || mime.contains("video/mp4v-es")) {
+ bitRate = ENCODE_MIN_BIT_RATE;
} else {
- Log.e(TAG, "Unable to create file to write encoder output: " +
- outEncodeFile.toString());
+ bitRate = ENCODE_DEFAULT_BIT_RATE;
}
}
- File rawFile =
- new File(mContext.getFilesDir() + "/decoder_" + tid + ".out");
- if (rawFile.exists()) {
- if (DEBUG) {
- Log.i(TAG, "Path of decoded input file: " + rawFile.toString());
- }
- FileInputStream eleStream = new FileInputStream(rawFile);
- if (mime.startsWith("video/")) {
- width = format.getInteger(MediaFormat.KEY_WIDTH);
- height = format.getInteger(MediaFormat.KEY_HEIGHT);
- if (format.containsKey(MediaFormat.KEY_FRAME_RATE)) {
- frameRate = format.getInteger(MediaFormat.KEY_FRAME_RATE);
- } else if (frameRate <= 0) {
- frameRate = ENCODE_DEFAULT_FRAME_RATE;
- }
- if (format.containsKey(MediaFormat.KEY_BIT_RATE)) {
- bitRate = format.getInteger(MediaFormat.KEY_BIT_RATE);
- } else if (bitRate <= 0) {
- if (mime.contains("video/3gpp") ||
- mime.contains("video/mp4v-es")) {
- bitRate = ENCODE_MIN_BIT_RATE;
- } else {
- bitRate = ENCODE_DEFAULT_BIT_RATE;
- }
- }
- if (format.containsKey(MediaFormat.KEY_PROFILE)) {
- profile = format.getInteger(MediaFormat.KEY_PROFILE);
- }
- if (format.containsKey(MediaFormat.KEY_PROFILE)) {
- level = format.getInteger(MediaFormat.KEY_LEVEL);
- }
- } else {
- sampleRate = format.getInteger(MediaFormat.KEY_SAMPLE_RATE);
- numChannels = format.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
- bitRate = sampleRate * numChannels * 16;
- }
- /*Setup Encode Format*/
- MediaFormat encodeFormat;
- if (mime.startsWith("video/")) {
- frameSize = width * height * 3 / 2;
- encodeFormat = MediaFormat.createVideoFormat(mime, width, height);
- encodeFormat.setInteger(MediaFormat.KEY_FRAME_RATE, frameRate);
- encodeFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitRate);
- encodeFormat.setInteger(MediaFormat.KEY_PROFILE, profile);
- encodeFormat.setInteger(MediaFormat.KEY_LEVEL, level);
- encodeFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1);
- encodeFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, frameSize);
- } else {
- encodeFormat = MediaFormat
- .createAudioFormat(mime, sampleRate, numChannels);
- encodeFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitRate);
- frameSize = 4096;
- }
- Encoder encoder = new Encoder();
- encoder.setupEncoder(encodeOutputStream, eleStream);
- status = encoder.encode(codecName, encodeFormat, mime, frameRate,
- sampleRate, frameSize, asyncMode);
- encoder.deInitEncoder();
- if (status == 0) {
- encoder.dumpStatistics(mInputFile + "with " + codecName + " for " +
- "aSyncMode = " + asyncMode, extractor.getClipDuration());
- Log.i(TAG, "Encoding complete for file: " + mInputFile +
- " with codec: " + codecName + " for aSyncMode = " +
- asyncMode);
- } else {
- Log.e(TAG,
- codecName + " encoder returned error " + status + " for " +
- "file:" + " " + mInputFile);
- }
- encoder.resetEncoder();
- eleStream.close();
- if (encodeOutputStream != null) {
- encodeOutputStream.close();
- }
+ if (format.containsKey(MediaFormat.KEY_PROFILE)) {
+ profile = format.getInteger(MediaFormat.KEY_PROFILE);
}
- }
- }
- //Cleanup temporary input file
- if (decodedFile.exists()) {
- if (decodedFile.delete()) {
- Log.i(TAG, "Successfully deleted decoded file");
+ if (format.containsKey(MediaFormat.KEY_PROFILE)) {
+ level = format.getInteger(MediaFormat.KEY_LEVEL);
+ }
} else {
- Log.e(TAG, "Unable to delete decoded file");
+ sampleRate = format.getInteger(MediaFormat.KEY_SAMPLE_RATE);
+ numChannels = format.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
+ bitRate = sampleRate * numChannels * 16;
}
+ /*Setup Encode Format*/
+ MediaFormat encodeFormat;
+ if (mime.startsWith("video/")) {
+ frameSize = width * height * 3 / 2;
+ encodeFormat = MediaFormat.createVideoFormat(mime, width, height);
+ encodeFormat.setInteger(MediaFormat.KEY_FRAME_RATE, frameRate);
+ encodeFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitRate);
+ encodeFormat.setInteger(MediaFormat.KEY_PROFILE, profile);
+ encodeFormat.setInteger(MediaFormat.KEY_LEVEL, level);
+ encodeFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1);
+ encodeFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, frameSize);
+ } else {
+ encodeFormat = MediaFormat.createAudioFormat(mime, sampleRate, numChannels);
+ encodeFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitRate);
+ frameSize = 4096;
+ }
+ Encoder encoder = new Encoder();
+ encoder.setupEncoder(encodeOutputStream, eleStream);
+ status = encoder.encode(codecName, encodeFormat, mime, frameRate, sampleRate,
+ frameSize, asyncMode);
+ encoder.deInitEncoder();
+ assertEquals(
+ codecName + " encoder returned error " + status + " for " + "file:" +
+ " " + mInputFile, 0, status);
+ encoder.dumpStatistics(mInputFile, codecName, (asyncMode ? "async" : "sync"),
+ extractor.getClipDuration(), mStatsFile);
+ Log.i(TAG, "Encoding complete for file: " + mInputFile + " with codec: " +
+ codecName + " for aSyncMode = " + asyncMode);
+ encoder.resetEncoder();
+ eleStream.close();
+ if (encodeOutputStream != null) {
+ encodeOutputStream.close();
+ }
+
}
}
- extractor.deinitExtractor();
- fileInput.close();
- } else {
- Log.w(TAG, "Warning: Test Skipped. Cannot find " + mInputFile + " in directory " +
- mInputFilePath);
+ //Cleanup temporary input file
+ if (decodedFile.exists()) {
+ assertTrue(" Unable to delete decoded file" + decodedFile.toString(),
+ decodedFile.delete());
+ Log.i(TAG, "Successfully deleted decoded file");
+ }
}
+ extractor.deinitExtractor();
+ fileInput.close();
+ }
+
+ @Test
+ public void testNativeEncoder() throws Exception {
+ File inputFile = new File(mInputFilePath + mInputFile);
+ assertTrue("Cannot find " + mInputFile + " in directory " + mInputFilePath,
+ inputFile.exists());
+ int tid = android.os.Process.myTid();
+ final String mDecodedFile = mContext.getFilesDir() + "/decoder_" + tid + ".out";
+ FileInputStream fileInput = new FileInputStream(inputFile);
+ FileDescriptor fileDescriptor = fileInput.getFD();
+ Extractor extractor = new Extractor();
+ int trackCount = extractor.setUpExtractor(fileDescriptor);
+ assertTrue("Extraction failed. No tracks for file: ", trackCount > 0);
+ for (int currentTrack = 0; currentTrack < trackCount; currentTrack++) {
+ extractor.selectExtractorTrack(currentTrack);
+ MediaFormat format = extractor.getFormat(currentTrack);
+ String mime = format.getString(MediaFormat.KEY_MIME);
+ ArrayList<String> mediaCodecs = CodecUtils.selectCodecs(mime, true);
+ // Encoding the decoder's output
+ for (String codecName : mediaCodecs) {
+ Native nativeEncoder = new Native();
+ int status = nativeEncoder.Encode(
+ mInputFilePath, mInputFile, mDecodedFile, mStatsFile, codecName);
+ assertEquals(
+ codecName + " encoder returned error " + status + " for " + "file:" + " " +
+ mInputFile, 0, status);
+ }
+ }
+ File decodedFile = new File(mDecodedFile);
+ // Cleanup temporary input file
+ if (decodedFile.exists()) {
+ assertTrue("Unable to delete - " + mDecodedFile, decodedFile.delete());
+ Log.i(TAG, "Successfully deleted - " + mDecodedFile);
+ }
+ fileInput.close();
}
}
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/ExtractorTest.java b/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/ExtractorTest.java
index a02011c..a33ecfe 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/ExtractorTest.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/ExtractorTest.java
@@ -18,12 +18,16 @@
import com.android.media.benchmark.R;
import com.android.media.benchmark.library.Extractor;
+import com.android.media.benchmark.library.Native;
+import com.android.media.benchmark.library.Stats;
import android.content.Context;
+import android.media.MediaFormat;
import android.util.Log;
import androidx.test.platform.app.InstrumentationRegistry;
+import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -31,19 +35,23 @@
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
-import static org.hamcrest.CoreMatchers.equalTo;
-import static org.hamcrest.CoreMatchers.is;
-import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
+
+import static org.junit.Assert.assertTrue;
@RunWith(Parameterized.class)
public class ExtractorTest {
private static Context mContext =
InstrumentationRegistry.getInstrumentation().getTargetContext();
private static final String mInputFilePath = mContext.getString(R.string.input_file_path);
+ private static final String mStatsFile =
+ mContext.getFilesDir() + "/Extractor." + System.currentTimeMillis() + ".csv";
private static final String TAG = "ExtractorTest";
private String mInputFileName;
private int mTrackId;
@@ -71,22 +79,42 @@
this.mTrackId = track;
}
+ @BeforeClass
+ public static void writeStatsHeaderToFile() throws IOException {
+ Stats mStats = new Stats();
+ boolean status = mStats.writeStatsHeader(mStatsFile);
+ assertTrue("Unable to open stats file for writing!", status);
+ }
+
@Test
public void sampleExtractTest() throws IOException {
- int status = -1;
File inputFile = new File(mInputFilePath + mInputFileName);
- if (inputFile.exists()) {
- FileInputStream fileInput = new FileInputStream(inputFile);
- FileDescriptor fileDescriptor = fileInput.getFD();
- Extractor extractor = new Extractor();
- extractor.setUpExtractor(fileDescriptor);
- status = extractor.extractSample(mTrackId);
- extractor.deinitExtractor();
- extractor.dumpStatistics(mInputFileName);
- fileInput.close();
- } else {
- Log.e(TAG, "Cannot find " + mInputFileName + " in directory " + mInputFilePath);
- }
- assertThat(status, is(equalTo(0)));
+ assertTrue("Cannot find " + mInputFileName + " in directory " + mInputFilePath,
+ inputFile.exists());
+ FileInputStream fileInput = new FileInputStream(inputFile);
+ FileDescriptor fileDescriptor = fileInput.getFD();
+ Extractor extractor = new Extractor();
+ extractor.setUpExtractor(fileDescriptor);
+ MediaFormat format = extractor.getFormat(mTrackId);
+ String mime = format.getString(MediaFormat.KEY_MIME);
+ int status = extractor.extractSample(mTrackId);
+ assertEquals("Extraction failed for " + mInputFileName, 0, status);
+ Log.i(TAG, "Extracted " + mInputFileName + " successfully.");
+ extractor.deinitExtractor();
+ extractor.dumpStatistics(mInputFileName, mime, mStatsFile);
+ fileInput.close();
+ }
+
+ @Test
+ public void sampleExtractNativeTest() throws IOException {
+ Native nativeExtractor = new Native();
+ File inputFile = new File(mInputFilePath + mInputFileName);
+ assertTrue("Cannot find " + mInputFileName + " in directory " + mInputFilePath,
+ inputFile.exists());
+ FileInputStream fileInput = new FileInputStream(inputFile);
+ int status = nativeExtractor.Extract(mInputFilePath, mInputFileName, mStatsFile);
+ fileInput.close();
+ assertEquals("Extraction failed for " + mInputFileName, 0, status);
+ Log.i(TAG, "Extracted " + mInputFileName + " successfully.");
}
}
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/MuxerTest.java b/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/MuxerTest.java
index 8c3080c..b69c57b 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/MuxerTest.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/MuxerTest.java
@@ -18,6 +18,8 @@
import com.android.media.benchmark.R;
import com.android.media.benchmark.library.Extractor;
import com.android.media.benchmark.library.Muxer;
+import com.android.media.benchmark.library.Native;
+import com.android.media.benchmark.library.Stats;
import androidx.test.platform.app.InstrumentationRegistry;
@@ -27,6 +29,7 @@
import android.media.MediaMuxer;
import android.util.Log;
+import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -34,6 +37,7 @@
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
@@ -42,15 +46,19 @@
import java.util.Hashtable;
import java.util.Map;
-import static org.hamcrest.CoreMatchers.equalTo;
-import static org.hamcrest.CoreMatchers.is;
-import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import static org.junit.Assert.assertTrue;
@RunWith(Parameterized.class)
public class MuxerTest {
private static Context mContext =
InstrumentationRegistry.getInstrumentation().getTargetContext();
private static final String mInputFilePath = mContext.getString(R.string.input_file_path);
+ private static final String mStatsFile =
+ mContext.getFilesDir() + "/Muxer." + System.currentTimeMillis() + ".csv";
private static final String TAG = "MuxerTest";
private static final Map<String, Integer> mMapFormat = new Hashtable<String, Integer>() {
{
@@ -93,60 +101,79 @@
this.mFormat = outputFormat;
}
+ @BeforeClass
+ public static void writeStatsHeaderToFile() throws IOException {
+ Stats mStats = new Stats();
+ boolean status = mStats.writeStatsHeader(mStatsFile);
+ assertTrue("Unable to open stats file for writing!", status);
+ }
+
@Test
public void sampleMuxerTest() throws IOException {
- int status = -1;
File inputFile = new File(mInputFilePath + mInputFileName);
- if (inputFile.exists()) {
- FileInputStream fileInput = new FileInputStream(inputFile);
- FileDescriptor fileDescriptor = fileInput.getFD();
- ArrayList<ByteBuffer> inputBuffer = new ArrayList<>();
- ArrayList<MediaCodec.BufferInfo> inputBufferInfo = new ArrayList<>();
- Extractor extractor = new Extractor();
- int trackCount = extractor.setUpExtractor(fileDescriptor);
- for (int currentTrack = 0; currentTrack < trackCount; currentTrack++) {
- extractor.selectExtractorTrack(currentTrack);
- while (true) {
- int sampleSize = extractor.getFrameSample();
- MediaCodec.BufferInfo bufferInfo = extractor.getBufferInfo();
- MediaCodec.BufferInfo tempBufferInfo = new MediaCodec.BufferInfo();
- tempBufferInfo
- .set(bufferInfo.offset, bufferInfo.size, bufferInfo.presentationTimeUs,
- bufferInfo.flags);
- inputBufferInfo.add(tempBufferInfo);
- ByteBuffer tempSampleBuffer = ByteBuffer.allocate(tempBufferInfo.size);
- tempSampleBuffer.put(extractor.getFrameBuffer().array(), 0, bufferInfo.size);
- inputBuffer.add(tempSampleBuffer);
- if (sampleSize < 0) {
- break;
- }
- }
- MediaFormat format = extractor.getFormat(currentTrack);
- int outputFormat = mMapFormat.getOrDefault(mFormat, -1);
- if (outputFormat != -1) {
- Muxer muxer = new Muxer();
- int trackIndex = muxer.setUpMuxer(mContext, outputFormat, format);
- status = muxer.mux(trackIndex, inputBuffer, inputBufferInfo);
- if (status != 0) {
- Log.e(TAG, "Cannot perform write operation for " + mInputFileName);
- }
- muxer.deInitMuxer();
- muxer.dumpStatistics(mInputFileName, extractor.getClipDuration());
- muxer.resetMuxer();
- extractor.unselectExtractorTrack(currentTrack);
- inputBufferInfo.clear();
- inputBuffer.clear();
- } else {
- Log.e(TAG, "Test failed for " + mInputFileName + ". Returned invalid " +
- "output format for given " + mFormat + " format.");
+ assertTrue("Cannot find " + mInputFileName + " in directory " + mInputFilePath,
+ inputFile.exists());
+ FileInputStream fileInput = new FileInputStream(inputFile);
+ FileDescriptor fileDescriptor = fileInput.getFD();
+ ArrayList<ByteBuffer> inputBuffer = new ArrayList<>();
+ ArrayList<MediaCodec.BufferInfo> inputBufferInfo = new ArrayList<>();
+ Extractor extractor = new Extractor();
+ int trackCount = extractor.setUpExtractor(fileDescriptor);
+ for (int currentTrack = 0; currentTrack < trackCount; currentTrack++) {
+ extractor.selectExtractorTrack(currentTrack);
+ while (true) {
+ int sampleSize = extractor.getFrameSample();
+ MediaCodec.BufferInfo bufferInfo = extractor.getBufferInfo();
+ MediaCodec.BufferInfo tempBufferInfo = new MediaCodec.BufferInfo();
+ tempBufferInfo
+ .set(bufferInfo.offset, bufferInfo.size, bufferInfo.presentationTimeUs,
+ bufferInfo.flags);
+ inputBufferInfo.add(tempBufferInfo);
+ ByteBuffer tempSampleBuffer = ByteBuffer.allocate(tempBufferInfo.size);
+ tempSampleBuffer.put(extractor.getFrameBuffer().array(), 0, bufferInfo.size);
+ inputBuffer.add(tempSampleBuffer);
+ if (sampleSize < 0) {
+ break;
}
}
- extractor.deinitExtractor();
- fileInput.close();
- } else {
- Log.w(TAG, "Warning: Test Skipped. Cannot find " + mInputFileName + " in directory " +
- mInputFilePath);
+ MediaFormat format = extractor.getFormat(currentTrack);
+ int outputFormat = mMapFormat.getOrDefault(mFormat, -1);
+ assertNotEquals("Test failed for " + mInputFileName + ". Returned invalid " +
+ "output format for given " + mFormat + " format.", -1, outputFormat);
+ Muxer muxer = new Muxer();
+ int trackIndex = muxer.setUpMuxer(mContext, outputFormat, format);
+ int status = muxer.mux(trackIndex, inputBuffer, inputBufferInfo);
+ assertEquals("Cannot perform write operation for " + mInputFileName, 0, status);
+ Log.i(TAG, "Muxed " + mInputFileName + " successfully.");
+ muxer.deInitMuxer();
+ muxer.dumpStatistics(mInputFileName, mFormat, extractor.getClipDuration(), mStatsFile);
+ muxer.resetMuxer();
+ extractor.unselectExtractorTrack(currentTrack);
+ inputBufferInfo.clear();
+ inputBuffer.clear();
+
}
- assertThat(status, is(equalTo(0)));
+ extractor.deinitExtractor();
+ fileInput.close();
+ }
+
+ @Test
+ public void sampleMuxerNativeTest() {
+ Native nativeMuxer = new Native();
+ File inputFile = new File(mInputFilePath + mInputFileName);
+ assertTrue("Cannot find " + mInputFileName + " in directory " + mInputFilePath,
+ inputFile.exists());
+ int tid = android.os.Process.myTid();
+ String mMuxOutputFile = (mContext.getFilesDir() + "/mux_" + tid + ".out");
+ int status = nativeMuxer.Mux(
+ mInputFilePath, mInputFileName, mMuxOutputFile, mStatsFile, mFormat);
+ assertEquals("Cannot perform write operation for " + mInputFileName, 0, status);
+ Log.i(TAG, "Muxed " + mInputFileName + " successfully.");
+ File muxedFile = new File(mMuxOutputFile);
+ // Cleanup temporary output file
+ if (muxedFile.exists()) {
+ assertTrue("Unable to delete" + mMuxOutputFile + " file.",
+ muxedFile.delete());
+ }
}
}
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/Android.bp b/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/Android.bp
new file mode 100644
index 0000000..c72eb55
--- /dev/null
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/Android.bp
@@ -0,0 +1,32 @@
+cc_test_library {
+ name: "libmediabenchmark_jni",
+
+ defaults: [
+ "libmediabenchmark_common-defaults",
+ "libmediabenchmark_soft_sanitize_all-defaults",
+ ],
+
+ srcs: [
+ "NativeExtractor.cpp",
+ "NativeMuxer.cpp",
+ "NativeEncoder.cpp",
+ "NativeDecoder.cpp",
+ ],
+
+ shared_libs: [
+ "liblog",
+ ],
+
+ static_libs: [
+ "libmediabenchmark_common",
+ "libmediabenchmark_extractor",
+ "libmediabenchmark_muxer",
+ "libmediabenchmark_decoder",
+ "libmediabenchmark_encoder",
+ ],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+}
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/CMakeLists.txt b/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/CMakeLists.txt
new file mode 100644
index 0000000..5823883
--- /dev/null
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/CMakeLists.txt
@@ -0,0 +1,44 @@
+#
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+#
+
+cmake_minimum_required(VERSION 3.4.1)
+
+set(native_source_path "../../../../src/native")
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Werror")
+
+add_library(
+ mediabenchmark_jni SHARED
+ NativeExtractor.cpp
+ NativeMuxer.cpp
+ NativeDecoder.cpp
+ NativeEncoder.cpp
+ ${native_source_path}/common/BenchmarkCommon.cpp
+ ${native_source_path}/common/Stats.cpp
+ ${native_source_path}/common/utils/Timers.cpp
+ ${native_source_path}/extractor/Extractor.cpp
+ ${native_source_path}/muxer/Muxer.cpp
+ ${native_source_path}/decoder/Decoder.cpp
+ ${native_source_path}/encoder/Encoder.cpp)
+
+include_directories(${native_source_path}/common)
+include_directories(${native_source_path}/extractor)
+include_directories(${native_source_path}/muxer)
+include_directories(${native_source_path}/decoder)
+include_directories(${native_source_path}/encoder)
+
+find_library(log-lib log)
+
+target_link_libraries(mediabenchmark_jni mediandk ${log-lib})
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/NativeDecoder.cpp b/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/NativeDecoder.cpp
new file mode 100644
index 0000000..043bc9e
--- /dev/null
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/NativeDecoder.cpp
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "NativeDecoder"
+
+#include <jni.h>
+#include <fstream>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include <android/log.h>
+
+#include "Decoder.h"
+
+extern "C" JNIEXPORT int JNICALL Java_com_android_media_benchmark_library_Native_Decode(
+ JNIEnv *env, jobject thiz, jstring jFilePath, jstring jFileName, jstring jStatsFile,
+ jstring jCodecName, jboolean asyncMode) {
+ const char *filePath = env->GetStringUTFChars(jFilePath, nullptr);
+ const char *fileName = env->GetStringUTFChars(jFileName, nullptr);
+ string sFilePath = string(filePath) + string(fileName);
+ UNUSED(thiz);
+ FILE *inputFp = fopen(sFilePath.c_str(), "rb");
+ env->ReleaseStringUTFChars(jFileName, fileName);
+ env->ReleaseStringUTFChars(jFilePath, filePath);
+ if (!inputFp) {
+ ALOGE("Unable to open input file for reading");
+ return -1;
+ }
+
+ Decoder *decoder = new Decoder();
+ Extractor *extractor = decoder->getExtractor();
+ if (!extractor) {
+ ALOGE("Extractor creation failed");
+ return -1;
+ }
+
+ // Read file properties
+ struct stat buf;
+ stat(sFilePath.c_str(), &buf);
+ size_t fileSize = buf.st_size;
+ if (fileSize > kMaxBufferSize) {
+ ALOGE("File size greater than maximum buffer size");
+ return -1;
+ }
+ int32_t fd = fileno(inputFp);
+ int32_t trackCount = extractor->initExtractor(fd, fileSize);
+ if (trackCount <= 0) {
+ ALOGE("initExtractor failed");
+ return -1;
+ }
+ for (int curTrack = 0; curTrack < trackCount; curTrack++) {
+ int32_t status = extractor->setupTrackFormat(curTrack);
+ if (status != 0) {
+ ALOGE("Track Format invalid");
+ return -1;
+ }
+
+ uint8_t *inputBuffer = (uint8_t *) malloc(fileSize);
+ if (!inputBuffer) {
+ ALOGE("Insufficient memory");
+ return -1;
+ }
+
+ vector<AMediaCodecBufferInfo> frameInfo;
+ AMediaCodecBufferInfo info;
+ uint32_t inputBufferOffset = 0;
+
+ // Get frame data
+ while (1) {
+ status = extractor->getFrameSample(info);
+ if (status || !info.size) break;
+ // copy the meta data and buffer to be passed to decoder
+ if (inputBufferOffset + info.size > kMaxBufferSize) {
+ ALOGE("Memory allocated not sufficient");
+ free(inputBuffer);
+ return -1;
+ }
+ memcpy(inputBuffer + inputBufferOffset, extractor->getFrameBuf(), info.size);
+ frameInfo.push_back(info);
+ inputBufferOffset += info.size;
+ }
+
+ const char *codecName = env->GetStringUTFChars(jCodecName, nullptr);
+ string sCodecName = string(codecName);
+ decoder->setupDecoder();
+ status = decoder->decode(inputBuffer, frameInfo, sCodecName, asyncMode);
+ if (status != AMEDIA_OK) {
+ ALOGE("Decode returned error");
+ free(inputBuffer);
+ env->ReleaseStringUTFChars(jCodecName, codecName);
+ return -1;
+ }
+ decoder->deInitCodec();
+ const char *inputReference = env->GetStringUTFChars(jFileName, nullptr);
+ const char *statsFile = env->GetStringUTFChars(jStatsFile, nullptr);
+ string sInputReference = string(inputReference);
+ decoder->dumpStatistics(sInputReference, sCodecName, (asyncMode ? "async" : "sync"),
+ statsFile);
+ env->ReleaseStringUTFChars(jCodecName, codecName);
+ env->ReleaseStringUTFChars(jStatsFile, statsFile);
+ env->ReleaseStringUTFChars(jFileName, inputReference);
+ if (inputBuffer) {
+ free(inputBuffer);
+ inputBuffer = nullptr;
+ }
+ decoder->resetDecoder();
+ }
+ if (inputFp) {
+ fclose(inputFp);
+ inputFp = nullptr;
+ }
+ extractor->deInitExtractor();
+ delete decoder;
+ return 0;
+}
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/NativeEncoder.cpp b/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/NativeEncoder.cpp
new file mode 100644
index 0000000..271b852
--- /dev/null
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/NativeEncoder.cpp
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "NativeEncoder"
+
+#include <jni.h>
+#include <fstream>
+#include <iostream>
+#include <sys/stat.h>
+
+#include <android/log.h>
+
+#include "Decoder.h"
+#include "Encoder.h"
+
+#include <stdio.h>
+
+extern "C" JNIEXPORT int JNICALL Java_com_android_media_benchmark_library_Native_Encode(
+ JNIEnv *env, jobject thiz, jstring jFilePath, jstring jFileName, jstring jOutFilePath,
+ jstring jStatsFile, jstring jCodecName) {
+ const char *filePath = env->GetStringUTFChars(jFilePath, nullptr);
+ const char *fileName = env->GetStringUTFChars(jFileName, nullptr);
+ string sFilePath = string(filePath) + string(fileName);
+ UNUSED(thiz);
+ FILE *inputFp = fopen(sFilePath.c_str(), "rb");
+ env->ReleaseStringUTFChars(jFileName, fileName);
+ env->ReleaseStringUTFChars(jFilePath, filePath);
+ if (!inputFp) {
+ ALOGE("Unable to open input file for reading");
+ return -1;
+ }
+
+ Decoder *decoder = new Decoder();
+ Extractor *extractor = decoder->getExtractor();
+ if (!extractor) {
+ ALOGE("Extractor creation failed");
+ return -1;
+ }
+
+ // Read file properties
+ struct stat buf;
+ stat(sFilePath.c_str(), &buf);
+ size_t fileSize = buf.st_size;
+ if (fileSize > kMaxBufferSize) {
+ ALOGE("File size greater than maximum buffer size");
+ return -1;
+ }
+ int32_t fd = fileno(inputFp);
+ int32_t trackCount = extractor->initExtractor(fd, fileSize);
+ if (trackCount <= 0) {
+ ALOGE("initExtractor failed");
+ return -1;
+ }
+
+ for (int curTrack = 0; curTrack < trackCount; curTrack++) {
+ int32_t status = extractor->setupTrackFormat(curTrack);
+ if (status != 0) {
+ ALOGE("Track Format invalid");
+ return -1;
+ }
+ uint8_t *inputBuffer = (uint8_t *) malloc(fileSize);
+ if (!inputBuffer) {
+ ALOGE("Insufficient memory");
+ return -1;
+ }
+ vector<AMediaCodecBufferInfo> frameInfo;
+ AMediaCodecBufferInfo info;
+ uint32_t inputBufferOffset = 0;
+
+ // Get frame data
+ while (1) {
+ status = extractor->getFrameSample(info);
+ if (status || !info.size) break;
+ // copy the meta data and buffer to be passed to decoder
+ if (inputBufferOffset + info.size > kMaxBufferSize) {
+ ALOGE("Memory allocated not sufficient");
+ free(inputBuffer);
+ return -1;
+ }
+ memcpy(inputBuffer + inputBufferOffset, extractor->getFrameBuf(), info.size);
+ frameInfo.push_back(info);
+ inputBufferOffset += info.size;
+ }
+ string decName = "";
+ const char *outputFilePath = env->GetStringUTFChars(jOutFilePath, nullptr);
+ FILE *outFp = fopen(outputFilePath, "wb");
+ if (outFp == nullptr) {
+ ALOGE("%s - File failed to open for writing!", outputFilePath);
+ free(inputBuffer);
+ return -1;
+ }
+ decoder->setupDecoder();
+ status = decoder->decode(inputBuffer, frameInfo, decName, false /*asyncMode */, outFp);
+ if (status != AMEDIA_OK) {
+ ALOGE("Decode returned error");
+ free(inputBuffer);
+ return -1;
+ }
+ AMediaFormat *format = extractor->getFormat();
+ if (inputBuffer) {
+ free(inputBuffer);
+ inputBuffer = nullptr;
+ }
+ const char *mime = nullptr;
+ AMediaFormat_getString(format, AMEDIAFORMAT_KEY_MIME, &mime);
+ if (!mime) {
+ ALOGE("Error in AMediaFormat_getString");
+ return -1;
+ }
+ ifstream eleStream;
+ eleStream.open(outputFilePath, ifstream::binary | ifstream::ate);
+ if (!eleStream.is_open()) {
+ ALOGE("%s - File failed to open for reading!", outputFilePath);
+ env->ReleaseStringUTFChars(jOutFilePath, outputFilePath);
+ return -1;
+ }
+ const char *codecName = env->GetStringUTFChars(jCodecName, NULL);
+ const char *inputReference = env->GetStringUTFChars(jFileName, nullptr);
+ string sCodecName = string(codecName);
+ string sInputReference = string(inputReference);
+
+ bool asyncMode[2] = {true, false};
+ for (int i = 0; i < 2; i++) {
+ size_t eleSize = eleStream.tellg();
+ eleStream.seekg(0, ifstream::beg);
+
+ // Get encoder params
+ encParameter encParams;
+ if (!strncmp(mime, "video/", 6)) {
+ AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_WIDTH, &encParams.width);
+ AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_HEIGHT, &encParams.height);
+ AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_FRAME_RATE, &encParams.frameRate);
+ AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_BIT_RATE, &encParams.bitrate);
+ if (encParams.bitrate <= 0 || encParams.frameRate <= 0) {
+ encParams.frameRate = 25;
+ if (!strcmp(mime, "video/3gpp") || !strcmp(mime, "video/mp4v-es")) {
+ encParams.bitrate = 600000 /* 600 Kbps */;
+ } else {
+ encParams.bitrate = 8000000 /* 8 Mbps */;
+ }
+ }
+ AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_PROFILE, &encParams.profile);
+ AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_LEVEL, &encParams.level);
+ } else {
+ AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_SAMPLE_RATE, &encParams.sampleRate);
+ AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_CHANNEL_COUNT,
+ &encParams.numChannels);
+ encParams.bitrate =
+ encParams.sampleRate * encParams.numChannels * 16 /* bitsPerSample */;
+ }
+ Encoder *encoder = new Encoder();
+ encoder->setupEncoder();
+ status = encoder->encode(sCodecName, eleStream, eleSize, asyncMode[i], encParams,
+ (char *) mime);
+ encoder->deInitCodec();
+ cout << "codec : " << codecName << endl;
+ ALOGV(" asyncMode = %d \n", asyncMode[i]);
+ const char *statsFile = env->GetStringUTFChars(jStatsFile, nullptr);
+ encoder->dumpStatistics(sInputReference, extractor->getClipDuration(), sCodecName,
+ (asyncMode[i] ? "async" : "sync"), statsFile);
+ env->ReleaseStringUTFChars(jStatsFile, statsFile);
+ encoder->resetEncoder();
+ delete encoder;
+ encoder = nullptr;
+ }
+ eleStream.close();
+ if (outFp) {
+ fclose(outFp);
+ outFp = nullptr;
+ }
+ env->ReleaseStringUTFChars(jFileName, inputReference);
+ env->ReleaseStringUTFChars(jCodecName, codecName);
+ env->ReleaseStringUTFChars(jOutFilePath, outputFilePath);
+ if (format) {
+ AMediaFormat_delete(format);
+ format = nullptr;
+ }
+ decoder->deInitCodec();
+ decoder->resetDecoder();
+ }
+ if (inputFp) {
+ fclose(inputFp);
+ inputFp = nullptr;
+ }
+ extractor->deInitExtractor();
+ delete decoder;
+ return 0;
+}
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/NativeExtractor.cpp b/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/NativeExtractor.cpp
new file mode 100644
index 0000000..a762760
--- /dev/null
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/NativeExtractor.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "NativeExtractor"
+
+#include <jni.h>
+#include <fstream>
+#include <string>
+#include <sys/stat.h>
+
+#include "Extractor.h"
+
+extern "C" JNIEXPORT int32_t JNICALL Java_com_android_media_benchmark_library_Native_Extract(
+ JNIEnv *env, jobject thiz, jstring jInputFilePath, jstring jInputFileName,
+ jstring jStatsFile) {
+ UNUSED(thiz);
+ const char *inputFilePath = env->GetStringUTFChars(jInputFilePath, nullptr);
+ const char *inputFileName = env->GetStringUTFChars(jInputFileName, nullptr);
+ string sFilePath = string(inputFilePath) + string(inputFileName);
+ FILE *inputFp = fopen(sFilePath.c_str(), "rb");
+
+ // Read file properties
+ struct stat buf;
+ stat(sFilePath.c_str(), &buf);
+ size_t fileSize = buf.st_size;
+ int32_t fd = fileno(inputFp);
+
+ Extractor *extractObj = new Extractor();
+ int32_t trackCount = extractObj->initExtractor((long) fd, fileSize);
+ if (trackCount <= 0) {
+ ALOGE("initExtractor failed");
+ return -1;
+ }
+
+ int32_t trackID = 0;
+ const char *mime = nullptr;
+ int32_t status = extractObj->extract(trackID);
+ if (status != AMEDIA_OK) {
+ ALOGE("Extraction failed");
+ return -1;
+ }
+
+ if (inputFp) {
+ fclose(inputFp);
+ inputFp = nullptr;
+ }
+ status = extractObj->setupTrackFormat(trackID);
+ AMediaFormat *format = extractObj->getFormat();
+ if (!format) {
+ ALOGE("format is null!");
+ return -1;
+ }
+ AMediaFormat_getString(format, AMEDIAFORMAT_KEY_MIME, &mime);
+ if (!mime) {
+ ALOGE("mime is null!");
+ return -1;
+ }
+ extractObj->deInitExtractor();
+ const char *statsFile = env->GetStringUTFChars(jStatsFile, nullptr);
+ extractObj->dumpStatistics(string(inputFileName), string(mime), statsFile);
+ env->ReleaseStringUTFChars(jStatsFile, statsFile);
+ env->ReleaseStringUTFChars(jInputFilePath, inputFilePath);
+ env->ReleaseStringUTFChars(jInputFileName, inputFileName);
+
+ delete extractObj;
+ return status;
+}
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/NativeMuxer.cpp b/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/NativeMuxer.cpp
new file mode 100644
index 0000000..a5ef5b8
--- /dev/null
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/cpp/NativeMuxer.cpp
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "NativeMuxer"
+
+#include <jni.h>
+#include <fstream>
+#include <string>
+#include <sys/stat.h>
+
+#include "Muxer.h"
+
+MUXER_OUTPUT_T getMuxerOutFormat(const char *fmt);
+
+extern "C" JNIEXPORT int32_t JNICALL Java_com_android_media_benchmark_library_Native_Mux(
+ JNIEnv *env, jobject thiz, jstring jInputFilePath, jstring jInputFileName,
+ jstring jOutputFilePath, jstring jStatsFile, jstring jFormat) {
+ UNUSED(thiz);
+ ALOGV("Mux the samples given by extractor");
+ const char *inputFilePath = env->GetStringUTFChars(jInputFilePath, nullptr);
+ const char *inputFileName = env->GetStringUTFChars(jInputFileName, nullptr);
+ string sInputFile = string(inputFilePath) + string(inputFileName);
+ FILE *inputFp = fopen(sInputFile.c_str(), "rb");
+ if (!inputFp) {
+ ALOGE("Unable to open input file for reading");
+ return -1;
+ }
+
+ const char *fmt = env->GetStringUTFChars(jFormat, nullptr);
+ MUXER_OUTPUT_T outputFormat = getMuxerOutFormat(fmt);
+ if (outputFormat == MUXER_OUTPUT_FORMAT_INVALID) {
+ ALOGE("output format is MUXER_OUTPUT_FORMAT_INVALID");
+ return MUXER_OUTPUT_FORMAT_INVALID;
+ }
+
+ Muxer *muxerObj = new Muxer();
+ Extractor *extractor = muxerObj->getExtractor();
+ if (!extractor) {
+ ALOGE("Extractor creation failed");
+ return -1;
+ }
+
+ // Read file properties
+ struct stat buf;
+ stat(sInputFile.c_str(), &buf);
+ size_t fileSize = buf.st_size;
+ int32_t fd = fileno(inputFp);
+
+ int32_t trackCount = extractor->initExtractor(fd, fileSize);
+ if (trackCount <= 0) {
+ ALOGE("initExtractor failed");
+ return -1;
+ }
+
+ for (int curTrack = 0; curTrack < trackCount; curTrack++) {
+ int32_t status = extractor->setupTrackFormat(curTrack);
+ if (status != 0) {
+ ALOGE("Track Format invalid");
+ return -1;
+ }
+
+ uint8_t *inputBuffer = (uint8_t *) malloc(fileSize);
+ if (!inputBuffer) {
+ ALOGE("Allocation Failed");
+ return -1;
+ }
+ vector<AMediaCodecBufferInfo> frameInfos;
+ AMediaCodecBufferInfo info;
+ uint32_t inputBufferOffset = 0;
+
+ // Get Frame Data
+ while (1) {
+ status = extractor->getFrameSample(info);
+ if (status || !info.size) break;
+ // copy the meta data and buffer to be passed to muxer
+ if (inputBufferOffset + info.size > fileSize) {
+ ALOGE("Memory allocated not sufficient");
+ if (inputBuffer) {
+ free(inputBuffer);
+ inputBuffer = nullptr;
+ }
+ return -1;
+ }
+ memcpy(inputBuffer + inputBufferOffset, extractor->getFrameBuf(),
+ static_cast<size_t>(info.size));
+ info.offset = inputBufferOffset;
+ frameInfos.push_back(info);
+ inputBufferOffset += info.size;
+ }
+
+ const char *outputFilePath = env->GetStringUTFChars(jOutputFilePath, nullptr);
+ FILE *outputFp = fopen(((string) outputFilePath).c_str(), "w+b");
+ env->ReleaseStringUTFChars(jOutputFilePath, outputFilePath);
+
+ if (!outputFp) {
+ ALOGE("Unable to open output file for writing");
+ if (inputBuffer) {
+ free(inputBuffer);
+ inputBuffer = nullptr;
+ }
+ return -1;
+ }
+ int32_t outFd = fileno(outputFp);
+
+ status = muxerObj->initMuxer(outFd, (MUXER_OUTPUT_T) outputFormat);
+ if (status != 0) {
+ ALOGE("initMuxer failed");
+ if (inputBuffer) {
+ free(inputBuffer);
+ inputBuffer = nullptr;
+ }
+ return -1;
+ }
+
+ status = muxerObj->mux(inputBuffer, frameInfos);
+ if (status != 0) {
+ ALOGE("Mux failed");
+ if (inputBuffer) {
+ free(inputBuffer);
+ inputBuffer = nullptr;
+ }
+ return -1;
+ }
+ muxerObj->deInitMuxer();
+ const char *statsFile = env->GetStringUTFChars(jStatsFile, nullptr);
+ string muxFormat(fmt);
+ muxerObj->dumpStatistics(string(inputFileName), muxFormat, statsFile);
+ env->ReleaseStringUTFChars(jStatsFile, statsFile);
+ env->ReleaseStringUTFChars(jInputFilePath, inputFilePath);
+ env->ReleaseStringUTFChars(jInputFileName, inputFileName);
+
+ if (inputBuffer) {
+ free(inputBuffer);
+ inputBuffer = nullptr;
+ }
+ if (outputFp) {
+ fclose(outputFp);
+ outputFp = nullptr;
+ }
+ muxerObj->resetMuxer();
+ }
+ if (inputFp) {
+ fclose(inputFp);
+ inputFp = nullptr;
+ }
+ env->ReleaseStringUTFChars(jFormat, fmt);
+ extractor->deInitExtractor();
+ delete muxerObj;
+
+ return 0;
+}
+
+MUXER_OUTPUT_T getMuxerOutFormat(const char *fmt) {
+ static const struct {
+ const char *name;
+ int value;
+ } kFormatMaps[] = {{"mp4", MUXER_OUTPUT_FORMAT_MPEG_4},
+ {"webm", MUXER_OUTPUT_FORMAT_WEBM},
+ {"3gpp", MUXER_OUTPUT_FORMAT_3GPP},
+ {"ogg", MUXER_OUTPUT_FORMAT_OGG}};
+
+ int32_t muxOutputFormat = MUXER_OUTPUT_FORMAT_INVALID;
+ for (auto kFormatMap : kFormatMaps) {
+ if (!strcmp(fmt, kFormatMap.name)) {
+ muxOutputFormat = kFormatMap.value;
+ break;
+ }
+ }
+ return (MUXER_OUTPUT_T) muxOutputFormat;
+}
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Decoder.java b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Decoder.java
index 2cd27c2..3b1eed4 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Decoder.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Decoder.java
@@ -239,11 +239,16 @@
* Prints out the statistics in the information log
*
* @param inputReference The operation being performed, in this case decode
+ * @param componentName Name of the component/codec
+ * @param mode The operating mode: Sync/Async
* @param durationUs Duration of the clip in microseconds
+ * @param statsFile The output file where the stats data is written
*/
- public void dumpStatistics(String inputReference, long durationUs) {
+ public void dumpStatistics(String inputReference, String componentName, String mode,
+ long durationUs, String statsFile) throws IOException {
String operation = "decode";
- mStats.dumpStatistics(operation, inputReference, durationUs);
+ mStats.dumpStatistics(
+ inputReference, operation, componentName, mode, durationUs, statsFile);
}
/**
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Encoder.java b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Encoder.java
index 03db294..40cf8bd 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Encoder.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Encoder.java
@@ -326,12 +326,17 @@
/**
* Prints out the statistics in the information log
*
- * @param inputReference The operation being performed, in this case encode
+ * @param inputReference The operation being performed, in this case decode
+ * @param componentName Name of the component/codec
+ * @param mode The operating mode: Sync/Async
* @param durationUs Duration of the clip in microseconds
+ * @param statsFile The output file where the stats data is written
*/
- public void dumpStatistics(String inputReference, long durationUs) {
+ public void dumpStatistics(String inputReference, String componentName, String mode,
+ long durationUs, String statsFile) throws IOException {
String operation = "encode";
- mStats.dumpStatistics(operation, inputReference, durationUs);
+ mStats.dumpStatistics(
+ inputReference, operation, componentName, mode, durationUs, statsFile);
}
/**
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Extractor.java b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Extractor.java
index 459e2a9..f3024e7 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Extractor.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Extractor.java
@@ -167,9 +167,12 @@
* Write the benchmark logs for the given input file
*
* @param inputReference Name of the input file
+ * @param mimeType Mime type of the muxed file
+ * @param statsFile The output file where the stats data is written
*/
- public void dumpStatistics(String inputReference) {
+ public void dumpStatistics(String inputReference, String mimeType, String statsFile)
+ throws IOException {
String operation = "extract";
- mStats.dumpStatistics(operation, inputReference, mDurationUs);
+ mStats.dumpStatistics(inputReference, operation, mimeType, "", mDurationUs, statsFile);
}
}
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Muxer.java b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Muxer.java
index 49eaa1c..340b539 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Muxer.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Muxer.java
@@ -101,10 +101,13 @@
* Write the benchmark logs for the given input file
*
* @param inputReference Name of the input file
+ * @param muxFormat Format of the muxed output
* @param clipDuration Duration of the given inputReference file
+ * @param statsFile The output file where the stats data is written
*/
- public void dumpStatistics(String inputReference, long clipDuration) {
+ public void dumpStatistics(String inputReference, String muxFormat, long clipDuration,
+ String statsFile) throws IOException {
String operation = "mux";
- mStats.dumpStatistics(operation, inputReference, clipDuration);
+ mStats.dumpStatistics(inputReference, operation, muxFormat, "", clipDuration, statsFile);
}
}
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Native.java b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Native.java
new file mode 100644
index 0000000..38b608a
--- /dev/null
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Native.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.media.benchmark.library;
+
+public class Native {
+ static { System.loadLibrary("mediabenchmark_jni"); }
+
+ public native int Extract(String inputFilePath, String inputFileName, String statsFile);
+
+ public native int Mux(String inputFilePath, String inputFileName, String outputFilePath,
+ String statsFile, String format);
+
+ public native int Decode(String inputFilePath, String inputFileName, String statsFile,
+ String codecName, boolean asyncMode);
+
+ public native int Encode(String inputFilePath, String inputFileName, String outputFilePath,
+ String statsFile, String codecName);
+}
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Stats.java b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Stats.java
index 18ab5be..7245a3a 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Stats.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Stats.java
@@ -18,6 +18,10 @@
import android.util.Log;
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+import java.io.IOException;
import java.util.ArrayList;
/**
@@ -91,14 +95,38 @@
}
/**
+ * Writes the stats header to a file
+ * <p>
+ * \param statsFile file where the stats data is to be written
+ **/
+ public boolean writeStatsHeader(String statsFile) throws IOException {
+ File outputFile = new File(statsFile);
+ FileOutputStream out = new FileOutputStream(outputFile, true);
+ if (!outputFile.exists())
+ return false;
+ String statsHeader =
+ "currentTime, fileName, operation, componentName, NDK/SDK, sync/async, setupTime, "
+ + "destroyTime, minimumTime, maximumTime, "
+ + "averageTime, timeToProcess1SecContent, totalBytesProcessedPerSec, "
+ + "timeToFirstFrame, totalSizeInBytes, totalTime\n";
+ out.write(statsHeader.getBytes());
+ out.close();
+ return true;
+ }
+
+ /**
* Dumps the stats of the operation for a given input media.
* <p>
+ * \param inputReference input media
* \param operation describes the operation performed on the input media
* (i.e. extract/mux/decode/encode)
- * \param inputReference input media
- * \param durationUs is a duration of the input media in microseconds.
+ * \param componentName name of the codec/muxFormat/mime
+ * \param mode the operating mode: sync/async.
+ * \param durationUs is a duration of the input media in microseconds.
+ * \param statsFile the file where the stats data is to be written.
*/
- public void dumpStatistics(String operation, String inputReference, long durationUs) {
+ public void dumpStatistics(String inputReference, String operation, String componentName,
+ String mode, long durationUs, String statsFile) throws IOException {
if (mOutputTimer.size() == 0) {
Log.e(TAG, "No output produced");
return;
@@ -121,18 +149,30 @@
maxTimeTakenNs = intervalNs;
}
}
- // Print the Stats
- Log.i(TAG, "Input Reference : " + inputReference);
- Log.i(TAG, "Setup Time in nano sec : " + mInitTimeNs);
- Log.i(TAG, "Average Time in nano sec : " + totalTimeTakenNs / mOutputTimer.size());
- Log.i(TAG, "Time to first frame in nano sec : " + timeToFirstFrameNs);
- Log.i(TAG, "Time taken (in nano sec) to " + operation + " 1 sec of content : " +
- timeTakenPerSec);
- Log.i(TAG, "Total bytes " + operation + "ed : " + size);
- Log.i(TAG, "Number of bytes " + operation + "ed per second : " +
- (size * 1000000000) / totalTimeTakenNs);
- Log.i(TAG, "Minimum Time in nano sec : " + minTimeTakenNs);
- Log.i(TAG, "Maximum Time in nano sec : " + maxTimeTakenNs);
- Log.i(TAG, "Destroy Time in nano sec : " + mDeInitTimeNs);
+
+ // Write the stats row data to file
+ String rowData = "";
+ rowData += System.nanoTime() + ", ";
+ rowData += inputReference + ", ";
+ rowData += operation + ", ";
+ rowData += componentName + ", ";
+ rowData += "SDK, ";
+ rowData += mode + ", ";
+ rowData += mInitTimeNs + ", ";
+ rowData += mDeInitTimeNs + ", ";
+ rowData += minTimeTakenNs + ", ";
+ rowData += maxTimeTakenNs + ", ";
+ rowData += totalTimeTakenNs / mOutputTimer.size() + ", ";
+ rowData += timeTakenPerSec + ", ";
+ rowData += (size * 1000000000) / totalTimeTakenNs + ", ";
+ rowData += timeToFirstFrameNs + ", ";
+ rowData += size + ", ";
+ rowData += totalTimeTakenNs + "\n";
+
+ File outputFile = new File(statsFile);
+ FileOutputStream out = new FileOutputStream(outputFile, true);
+ assert outputFile.exists() : "Failed to open the stats file for writing!";
+ out.write(rowData.getBytes());
+ out.close();
}
-}
\ No newline at end of file
+}
diff --git a/media/tests/benchmark/src/native/common/Android.bp b/media/tests/benchmark/src/native/common/Android.bp
index babc329..f8ea25c 100644
--- a/media/tests/benchmark/src/native/common/Android.bp
+++ b/media/tests/benchmark/src/native/common/Android.bp
@@ -29,7 +29,7 @@
export_include_dirs: ["."],
- ldflags: ["-Wl,-Bsymbolic"]
+ ldflags: ["-Wl,-Bsymbolic"],
}
cc_defaults {
@@ -55,7 +55,7 @@
cflags: [
"-Wall",
"-Werror",
- ]
+ ],
}
cc_library_static {
@@ -66,18 +66,20 @@
srcs: [
"BenchmarkC2Common.cpp",
+ "BenchmarkCommon.cpp",
+ "Stats.cpp",
+ "utils/Timers.cpp",
],
export_include_dirs: ["."],
- ldflags: ["-Wl,-Bsymbolic"]
+ ldflags: ["-Wl,-Bsymbolic"],
}
cc_defaults {
name: "libmediabenchmark_codec2_common-defaults",
defaults: [
- "libmediabenchmark_common-defaults",
"libcodec2-hidl-client-defaults",
"libmediabenchmark_soft_sanitize_all-defaults",
],
@@ -88,7 +90,14 @@
shared_libs: [
"libcodec2_client",
- ]
+ "libmediandk",
+ "liblog",
+ ],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
}
// public dependency for native implementation
@@ -102,5 +111,5 @@
"signed-integer-overflow",
],
cfi: true,
- }
+ },
}
diff --git a/media/tests/benchmark/src/native/common/BenchmarkCommon.cpp b/media/tests/benchmark/src/native/common/BenchmarkCommon.cpp
index ab74508..cb49b8e 100644
--- a/media/tests/benchmark/src/native/common/BenchmarkCommon.cpp
+++ b/media/tests/benchmark/src/native/common/BenchmarkCommon.cpp
@@ -56,6 +56,7 @@
void OnErrorCB(AMediaCodec *codec, void *userdata, media_status_t err, int32_t actionCode,
const char *detail) {
+ (void)codec;
ALOGE("OnErrorCB: err(%d), actionCode(%d), detail(%s)", err, actionCode, detail);
CallBackHandle *self = (CallBackHandle *)userdata;
self->mSawError = true;
@@ -91,7 +92,7 @@
/* Configure codec with the given format*/
const char *s = AMediaFormat_toString(format);
- ALOGV("Input format: %s\n", s);
+ ALOGI("Input format: %s\n", s);
media_status_t status = AMediaCodec_configure(codec, format, nullptr, nullptr, isEncoder);
if (status != AMEDIA_OK) {
@@ -99,4 +100,4 @@
return nullptr;
}
return codec;
-}
\ No newline at end of file
+}
diff --git a/media/tests/benchmark/src/native/common/BenchmarkCommon.h b/media/tests/benchmark/src/native/common/BenchmarkCommon.h
index e13158a..c11fe36 100644
--- a/media/tests/benchmark/src/native/common/BenchmarkCommon.h
+++ b/media/tests/benchmark/src/native/common/BenchmarkCommon.h
@@ -17,6 +17,7 @@
#ifndef __BENCHMARK_COMMON_H__
#define __BENCHMARK_COMMON_H__
+#include <sys/stat.h>
#include <inttypes.h>
#include <mutex>
#include <queue>
@@ -27,6 +28,7 @@
#include <media/NdkMediaError.h>
#include "Stats.h"
+#define UNUSED(x) (void)(x)
using namespace std;
diff --git a/media/tests/benchmark/src/native/common/Stats.cpp b/media/tests/benchmark/src/native/common/Stats.cpp
index 2d9bb31..bfde125 100644
--- a/media/tests/benchmark/src/native/common/Stats.cpp
+++ b/media/tests/benchmark/src/native/common/Stats.cpp
@@ -17,8 +17,10 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "Stats"
+#include <ctime>
#include <iostream>
#include <stdint.h>
+#include <fstream>
#include "Stats.h"
@@ -28,16 +30,20 @@
* \param operation describes the operation performed on the input media
* (i.e. extract/mux/decode/encode)
* \param inputReference input media
- * \param duarationUs is a duration of the input media in microseconds.
+ * \param durationUs is a duration of the input media in microseconds.
+ * \param componentName describes the codecName/muxFormat/mimeType.
+ * \param mode the operating mode: sync/async.
+ * \param statsFile the file where the stats data is to be written.
*/
-void Stats::dumpStatistics(std::string operation, std::string inputReference, int64_t duarationUs) {
+void Stats::dumpStatistics(string operation, string inputReference, int64_t durationUs,
+ string componentName, string mode, string statsFile) {
ALOGV("In %s", __func__);
if (!mOutputTimer.size()) {
ALOGE("No output produced");
return;
}
nsecs_t totalTimeTakenNs = getTotalTime();
- nsecs_t timeTakenPerSec = (totalTimeTakenNs * 1000000) / duarationUs;
+ nsecs_t timeTakenPerSec = (totalTimeTakenNs * 1000000) / durationUs;
nsecs_t timeToFirstFrameNs = *mOutputTimer.begin() - mStartTimeNs;
int32_t size = std::accumulate(mFrameSizes.begin(), mFrameSizes.end(), 0);
// get min and max output intervals.
@@ -52,15 +58,32 @@
else if (maxTimeTakenNs < intervalNs) maxTimeTakenNs = intervalNs;
}
- // Print the Stats
- std::cout << "Input Reference : " << inputReference << endl;
- std::cout << "Setup Time in nano sec : " << mInitTimeNs << endl;
- std::cout << "Average Time in nano sec : " << totalTimeTakenNs / mOutputTimer.size() << endl;
- std::cout << "Time to first frame in nano sec : " << timeToFirstFrameNs << endl;
- std::cout << "Time taken (in nano sec) to " << operation
- << " 1 sec of content : " << timeTakenPerSec << endl;
- std::cout << "Total bytes " << operation << "ed : " << size << endl;
- std::cout << "Minimum Time in nano sec : " << minTimeTakenNs << endl;
- std::cout << "Maximum Time in nano sec : " << maxTimeTakenNs << endl;
- std::cout << "Destroy Time in nano sec : " << mDeInitTimeNs << endl;
+ // Write the stats data to file.
+ int64_t dataSize = size;
+ int64_t bytesPerSec = ((int64_t)dataSize * 1000000000) / totalTimeTakenNs;
+ string rowData = "";
+ rowData.append(to_string(systemTime(CLOCK_MONOTONIC)) + ", ");
+ rowData.append(inputReference + ", ");
+ rowData.append(operation + ", ");
+ rowData.append(componentName + ", ");
+ rowData.append("NDK, ");
+ rowData.append(mode + ", ");
+ rowData.append(to_string(mInitTimeNs) + ", ");
+ rowData.append(to_string(mDeInitTimeNs) + ", ");
+ rowData.append(to_string(minTimeTakenNs) + ", ");
+ rowData.append(to_string(maxTimeTakenNs) + ", ");
+ rowData.append(to_string(totalTimeTakenNs / mOutputTimer.size()) + ", ");
+ rowData.append(to_string(timeTakenPerSec) + ", ");
+ rowData.append(to_string(bytesPerSec) + ", ");
+ rowData.append(to_string(timeToFirstFrameNs) + ", ");
+ rowData.append(to_string(size) + ",");
+ rowData.append(to_string(totalTimeTakenNs) + ",\n");
+
+ ofstream out(statsFile, ios::out | ios::app);
+ if(out.bad()) {
+ ALOGE("Failed to open stats file for writing!");
+ return;
+ }
+ out << rowData;
+ out.close();
}
diff --git a/media/tests/benchmark/src/native/common/Stats.h b/media/tests/benchmark/src/native/common/Stats.h
index 2f556ee..18e4b06 100644
--- a/media/tests/benchmark/src/native/common/Stats.h
+++ b/media/tests/benchmark/src/native/common/Stats.h
@@ -18,6 +18,7 @@
#define __STATS_H__
#include <android/log.h>
+#include <inttypes.h>
#ifndef ALOG
#define ALOG(priority, tag, ...) ((void)__android_log_print(ANDROID_##priority, tag, __VA_ARGS__))
@@ -25,6 +26,12 @@
#define ALOGI(...) ALOG(LOG_INFO, LOG_TAG, __VA_ARGS__)
#define ALOGE(...) ALOG(LOG_ERROR, LOG_TAG, __VA_ARGS__)
#define ALOGD(...) ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)
+#define ALOGW(...) ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__)
+
+#ifndef LOG_NDEBUG
+#define LOG_NDEBUG 1
+#endif
+
#if LOG_NDEBUG
#define ALOGV(cond, ...) ((void)0)
#else
@@ -95,7 +102,8 @@
return (*(mOutputTimer.end() - 1) - mStartTimeNs);
}
- void dumpStatistics(std::string operation, std::string inputReference, int64_t duarationUs);
+ void dumpStatistics(string operation, string inputReference, int64_t duarationUs,
+ string codecName = "", string mode = "", string statsFile = "");
};
#endif // __STATS_H__
diff --git a/media/tests/benchmark/src/native/decoder/Android.bp b/media/tests/benchmark/src/native/decoder/Android.bp
index b5072ab..9791c11 100644
--- a/media/tests/benchmark/src/native/decoder/Android.bp
+++ b/media/tests/benchmark/src/native/decoder/Android.bp
@@ -27,24 +27,26 @@
export_include_dirs: ["."],
- ldflags: ["-Wl,-Bsymbolic"]
+ ldflags: ["-Wl,-Bsymbolic"],
}
cc_library_static {
name: "libmediabenchmark_codec2_decoder",
defaults: [
- "libmediabenchmark_common-defaults",
"libmediabenchmark_codec2_common-defaults",
],
- srcs: ["C2Decoder.cpp"],
+ srcs: [
+ "C2Decoder.cpp",
+ "Decoder.cpp",
+ ],
static_libs: [
"libmediabenchmark_codec2_common",
- "libmediabenchmark_extractor",
+ "libmediabenchmark_codec2_extractor",
],
export_include_dirs: ["."],
- ldflags: ["-Wl,-Bsymbolic"]
+ ldflags: ["-Wl,-Bsymbolic"],
}
diff --git a/media/tests/benchmark/src/native/decoder/Decoder.cpp b/media/tests/benchmark/src/native/decoder/Decoder.cpp
index ac0d525..b797cc9 100644
--- a/media/tests/benchmark/src/native/decoder/Decoder.cpp
+++ b/media/tests/benchmark/src/native/decoder/Decoder.cpp
@@ -238,10 +238,11 @@
mStats->setDeInitTime(timeTaken);
}
-void Decoder::dumpStatistics(string inputReference) {
+void Decoder::dumpStatistics(string inputReference, string componentName, string mode,
+ string statsFile) {
int64_t durationUs = mExtractor->getClipDuration();
string operation = "decode";
- mStats->dumpStatistics(operation, inputReference, durationUs);
+ mStats->dumpStatistics(operation, inputReference, durationUs, componentName, mode, statsFile);
}
void Decoder::resetDecoder() {
diff --git a/media/tests/benchmark/src/native/decoder/Decoder.h b/media/tests/benchmark/src/native/decoder/Decoder.h
index aeda080..f3fa6a1 100644
--- a/media/tests/benchmark/src/native/decoder/Decoder.h
+++ b/media/tests/benchmark/src/native/decoder/Decoder.h
@@ -71,7 +71,8 @@
int32_t decode(uint8_t *inputBuffer, vector<AMediaCodecBufferInfo> &frameInfo,
string &codecName, bool asyncMode, FILE *outFp = nullptr);
- void dumpStatistics(string inputReference);
+ void dumpStatistics(string inputReference, string componentName = "", string mode = "",
+ string statsFile = "");
private:
AMediaCodec *mCodec;
diff --git a/media/tests/benchmark/src/native/encoder/Android.bp b/media/tests/benchmark/src/native/encoder/Android.bp
index 57865ef..8de7823 100644
--- a/media/tests/benchmark/src/native/encoder/Android.bp
+++ b/media/tests/benchmark/src/native/encoder/Android.bp
@@ -23,19 +23,19 @@
srcs: ["Encoder.cpp"],
- static_libs: ["libmediabenchmark_extractor",
- "libmediabenchmark_decoder",
+ static_libs: [
+ "libmediabenchmark_extractor",
+ "libmediabenchmark_decoder",
],
export_include_dirs: ["."],
- ldflags: ["-Wl,-Bsymbolic"]
+ ldflags: ["-Wl,-Bsymbolic"],
}
cc_library_static {
name: "libmediabenchmark_codec2_encoder",
defaults: [
- "libmediabenchmark_common-defaults",
"libmediabenchmark_codec2_common-defaults",
],
@@ -43,11 +43,11 @@
static_libs: [
"libmediabenchmark_codec2_common",
- "libmediabenchmark_extractor",
- "libmediabenchmark_decoder",
+ "libmediabenchmark_codec2_extractor",
+ "libmediabenchmark_codec2_decoder",
],
export_include_dirs: ["."],
- ldflags: ["-Wl,-Bsymbolic"]
+ ldflags: ["-Wl,-Bsymbolic"],
}
diff --git a/media/tests/benchmark/src/native/encoder/Encoder.cpp b/media/tests/benchmark/src/native/encoder/Encoder.cpp
index a5605de..f119cf3 100644
--- a/media/tests/benchmark/src/native/encoder/Encoder.cpp
+++ b/media/tests/benchmark/src/native/encoder/Encoder.cpp
@@ -174,9 +174,10 @@
memset(&mParams, 0, sizeof mParams);
}
-void Encoder::dumpStatistics(string inputReference, int64_t durationUs) {
+void Encoder::dumpStatistics(string inputReference, int64_t durationUs, string componentName,
+ string mode, string statsFile) {
string operation = "encode";
- mStats->dumpStatistics(operation, inputReference, durationUs);
+ mStats->dumpStatistics(operation, inputReference, durationUs, componentName, mode, statsFile);
}
int32_t Encoder::encode(string &codecName, ifstream &eleStream, size_t eleSize,
@@ -206,7 +207,7 @@
AMediaFormat_setInt32(mFormat, AMEDIAFORMAT_KEY_BIT_RATE, mParams.bitrate);
}
const char *s = AMediaFormat_toString(mFormat);
- ALOGV("Input format: %s\n", s);
+ ALOGI("Input format: %s\n", s);
int64_t sTime = mStats->getCurTime();
mCodec = createMediaCodec(mFormat, mMime, codecName, true /*isEncoder*/);
diff --git a/media/tests/benchmark/src/native/encoder/Encoder.h b/media/tests/benchmark/src/native/encoder/Encoder.h
index 6059c4a..3d12600 100644
--- a/media/tests/benchmark/src/native/encoder/Encoder.h
+++ b/media/tests/benchmark/src/native/encoder/Encoder.h
@@ -75,7 +75,8 @@
int32_t encode(std::string &codecName, std::ifstream &eleStream, size_t eleSize, bool asyncMode,
encParameter encParams, char *mime);
- void dumpStatistics(string inputReference, int64_t durationUs);
+ void dumpStatistics(string inputReference, int64_t durationUs, string codecName = "",
+ string mode = "", string statsFile = "");
private:
AMediaCodec *mCodec;
diff --git a/media/tests/benchmark/src/native/extractor/Android.bp b/media/tests/benchmark/src/native/extractor/Android.bp
index dfd0d49..7ed9476 100644
--- a/media/tests/benchmark/src/native/extractor/Android.bp
+++ b/media/tests/benchmark/src/native/extractor/Android.bp
@@ -27,3 +27,20 @@
ldflags: ["-Wl,-Bsymbolic"]
}
+
+cc_library_static {
+ name: "libmediabenchmark_codec2_extractor",
+ defaults: [
+ "libmediabenchmark_codec2_common-defaults",
+ ],
+
+ srcs: ["Extractor.cpp"],
+
+ static_libs: [
+ "libmediabenchmark_codec2_common",
+ ],
+
+ export_include_dirs: ["."],
+
+ ldflags: ["-Wl,-Bsymbolic"]
+}
diff --git a/media/tests/benchmark/src/native/extractor/Extractor.cpp b/media/tests/benchmark/src/native/extractor/Extractor.cpp
index b4cad0b..f0bb3b9 100644
--- a/media/tests/benchmark/src/native/extractor/Extractor.cpp
+++ b/media/tests/benchmark/src/native/extractor/Extractor.cpp
@@ -111,9 +111,9 @@
return AMEDIA_OK;
}
-void Extractor::dumpStatistics(string inputReference) {
+void Extractor::dumpStatistics(string inputReference, string componentName, string statsFile) {
string operation = "extract";
- mStats->dumpStatistics(operation, inputReference, mDurationUs);
+ mStats->dumpStatistics(operation, inputReference, mDurationUs, componentName, "", statsFile);
}
void Extractor::deInitExtractor() {
diff --git a/media/tests/benchmark/src/native/extractor/Extractor.h b/media/tests/benchmark/src/native/extractor/Extractor.h
index 4c39a72..1694fc7 100644
--- a/media/tests/benchmark/src/native/extractor/Extractor.h
+++ b/media/tests/benchmark/src/native/extractor/Extractor.h
@@ -45,7 +45,7 @@
int32_t extract(int32_t trackId);
- void dumpStatistics(std::string inputReference);
+ void dumpStatistics(string inputReference, string componentName = "", string statsFile = "");
void deInitExtractor();
diff --git a/media/tests/benchmark/src/native/muxer/Muxer.cpp b/media/tests/benchmark/src/native/muxer/Muxer.cpp
index b297a66..f8627cb 100644
--- a/media/tests/benchmark/src/native/muxer/Muxer.cpp
+++ b/media/tests/benchmark/src/native/muxer/Muxer.cpp
@@ -29,7 +29,7 @@
int64_t sTime = mStats->getCurTime();
mMuxer = AMediaMuxer_new(fd, (OutputFormat)outputFormat);
if (!mMuxer) {
- cout << "[ WARN ] Test Skipped. Unable to create muxer \n";
+ ALOGV("Unable to create muxer");
return AMEDIA_ERROR_INVALID_OBJECT;
}
/*
@@ -38,7 +38,7 @@
*/
ssize_t index = AMediaMuxer_addTrack(mMuxer, mFormat);
if (index < 0) {
- cout << "[ WARN ] Test Skipped. Format not supported \n";
+ ALOGV("Format not supported");
return index;
}
AMediaMuxer_start(mMuxer);
@@ -66,9 +66,10 @@
if (mStats) mStats->reset();
}
-void Muxer::dumpStatistics(string inputReference) {
+void Muxer::dumpStatistics(string inputReference, string componentName, string statsFile) {
string operation = "mux";
- mStats->dumpStatistics(operation, inputReference, mExtractor->getClipDuration());
+ mStats->dumpStatistics(operation, inputReference, mExtractor->getClipDuration(), componentName,
+ "", statsFile);
}
int32_t Muxer::mux(uint8_t *inputBuffer, vector<AMediaCodecBufferInfo> &frameInfos) {
diff --git a/media/tests/benchmark/src/native/muxer/Muxer.h b/media/tests/benchmark/src/native/muxer/Muxer.h
index eee3146..860fdaf 100644
--- a/media/tests/benchmark/src/native/muxer/Muxer.h
+++ b/media/tests/benchmark/src/native/muxer/Muxer.h
@@ -51,7 +51,7 @@
/* Process the frames and give Muxed output */
int32_t mux(uint8_t *inputBuffer, vector<AMediaCodecBufferInfo> &frameSizes);
- void dumpStatistics(string inputReference);
+ void dumpStatistics(string inputReference, string codecName = "", string statsFile = "");
private:
AMediaFormat *mFormat;
diff --git a/media/tests/benchmark/tests/Android.bp b/media/tests/benchmark/tests/Android.bp
index de6a8bf..f46fa4a 100644
--- a/media/tests/benchmark/tests/Android.bp
+++ b/media/tests/benchmark/tests/Android.bp
@@ -87,7 +87,7 @@
srcs: ["C2DecoderTest.cpp"],
static_libs: [
- "libmediabenchmark_extractor",
+ "libmediabenchmark_codec2_extractor",
"libmediabenchmark_codec2_common",
"libmediabenchmark_codec2_decoder",
],
@@ -103,8 +103,8 @@
srcs: ["C2EncoderTest.cpp"],
static_libs: [
- "libmediabenchmark_extractor",
- "libmediabenchmark_decoder",
+ "libmediabenchmark_codec2_extractor",
+ "libmediabenchmark_codec2_decoder",
"libmediabenchmark_codec2_common",
"libmediabenchmark_codec2_encoder",
],
diff --git a/media/tests/benchmark/tests/C2DecoderTest.cpp b/media/tests/benchmark/tests/C2DecoderTest.cpp
index 3531d8a..ecd9759 100644
--- a/media/tests/benchmark/tests/C2DecoderTest.cpp
+++ b/media/tests/benchmark/tests/C2DecoderTest.cpp
@@ -29,82 +29,64 @@
class C2DecoderTest : public ::testing::TestWithParam<pair<string, string>> {
public:
- C2DecoderTest() : mDecoder(nullptr), disableTest(false) { setupC2DecoderTest(); }
+ C2DecoderTest() : mDecoder(nullptr) {}
+
+ ~C2DecoderTest() {
+ if (!mCodecList.empty()) {
+ mCodecList.clear();
+ }
+ if (mDecoder) {
+ delete mDecoder;
+ mDecoder = nullptr;
+ }
+ }
+
+ virtual void SetUp() override { setupC2DecoderTest(); }
void setupC2DecoderTest();
vector<string> mCodecList;
C2Decoder *mDecoder;
- bool disableTest;
};
void C2DecoderTest::setupC2DecoderTest() {
mDecoder = new C2Decoder();
- if (!mDecoder) {
- cout << "[ WARN ] Test Skipped. C2Decoder creation failed\n";
- disableTest = true;
- return;
- }
+ ASSERT_NE(mDecoder, nullptr) << "C2Decoder creation failed";
+
int32_t status = mDecoder->setupCodec2();
- if (status != 0) {
- cout << "[ WARN ] Test Skipped. Codec2 setup failed \n";
- disableTest = true;
- return;
- }
+ ASSERT_EQ(status, 0) << "Codec2 setup failed";
+
mCodecList = mDecoder->getSupportedComponentList(false /* isEncoder*/);
- if (!mCodecList.size()) {
- cout << "[ WARN ] Test Skipped. Codec2 client didn't recognise any component \n";
- disableTest = true;
- return;
- }
+ ASSERT_GT(mCodecList.size(), 0) << "Codec2 client didn't recognise any component";
}
TEST_P(C2DecoderTest, Codec2Decode) {
- if (disableTest) return;
-
ALOGV("Decode the samples given by extractor using codec2");
string inputFile = gEnv->getRes() + GetParam().first;
FILE *inputFp = fopen(inputFile.c_str(), "rb");
- if (!inputFp) {
- cout << "[ WARN ] Test Skipped. Unable to open input file" << inputFile
- << " for reading \n";
- return;
- }
+ ASSERT_NE(inputFp, nullptr) << "Unable to open " << inputFile << " file for reading";
Extractor *extractor = new Extractor();
- if (!extractor) {
- cout << "[ WARN ] Test Skipped. Extractor creation failed \n";
- return;
- }
+ ASSERT_NE(extractor, nullptr) << "Extractor creation failed";
// Read file properties
- fseek(inputFp, 0, SEEK_END);
- size_t fileSize = ftell(inputFp);
- fseek(inputFp, 0, SEEK_SET);
+ struct stat buf;
+ stat(inputFile.c_str(), &buf);
+ size_t fileSize = buf.st_size;
int32_t fd = fileno(inputFp);
- if (fileSize > kMaxBufferSize) {
- cout << "[ WARN ] Test Skipped. Input file size is greater than the threshold memory "
- "dedicated to the test \n";
- }
+ ASSERT_LE(fileSize, kMaxBufferSize)
+ << "Input file size is greater than the threshold memory dedicated to the test";
int32_t trackCount = extractor->initExtractor(fd, fileSize);
- if (trackCount <= 0) {
- cout << "[ WARN ] Test Skipped. initExtractor failed\n";
- return;
- }
+ ASSERT_GT(trackCount, 0) << "initExtractor failed";
+
for (int32_t curTrack = 0; curTrack < trackCount; curTrack++) {
int32_t status = extractor->setupTrackFormat(curTrack);
- if (status != 0) {
- cout << "[ WARN ] Test Skipped. Track Format invalid \n";
- return;
- }
+ ASSERT_EQ(status, 0) << "Track Format invalid";
uint8_t *inputBuffer = (uint8_t *)malloc(fileSize);
- if (!inputBuffer) {
- cout << "[ WARN ] Test Skipped. Insufficient memory \n";
- return;
- }
+ ASSERT_NE(inputBuffer, nullptr) << "Insufficient memory";
vector<AMediaCodecBufferInfo> frameInfo;
AMediaCodecBufferInfo info;
@@ -116,11 +98,8 @@
void *csdBuffer = extractor->getCSDSample(info, idx);
if (!csdBuffer || !info.size) break;
// copy the meta data and buffer to be passed to decoder
- if (inputBufferOffset + info.size > fileSize) {
- cout << "[ WARN ] Test Skipped. Memory allocated not sufficient\n";
- free(inputBuffer);
- return;
- }
+ ASSERT_LE(inputBufferOffset + info.size, fileSize) << "Memory allocated not sufficient";
+
memcpy(inputBuffer + inputBufferOffset, csdBuffer, info.size);
frameInfo.push_back(info);
inputBufferOffset += info.size;
@@ -132,11 +111,8 @@
status = extractor->getFrameSample(info);
if (status || !info.size) break;
// copy the meta data and buffer to be passed to decoder
- if (inputBufferOffset + info.size > fileSize) {
- cout << "[ WARN ] Test Skipped. Memory allocated not sufficient\n";
- free(inputBuffer);
- return;
- }
+ ASSERT_LE(inputBufferOffset + info.size, fileSize) << "Memory allocated not sufficient";
+
memcpy(inputBuffer + inputBufferOffset, extractor->getFrameBuf(), info.size);
frameInfo.push_back(info);
inputBufferOffset += info.size;
@@ -148,21 +124,18 @@
if (codecName.find(GetParam().second) != string::npos &&
codecName.find("secure") == string::npos) {
status = mDecoder->createCodec2Component(codecName, format);
- if (status != 0) {
- cout << "[ WARN ] Test Skipped. Create component failed for " << codecName
- << "\n";
- continue;
- }
+ ASSERT_EQ(status, 0) << "Create component failed for " << codecName;
// Send the inputs to C2 Decoder and wait till all buffers are returned.
- mDecoder->decodeFrames(inputBuffer, frameInfo);
+ status = mDecoder->decodeFrames(inputBuffer, frameInfo);
+ ASSERT_EQ(status, 0) << "Decoder failed for " << codecName;
+
mDecoder->waitOnInputConsumption();
- if (!mDecoder->mEos) {
- cout << "[ WARN ] Test Failed. Didn't receive EOS \n";
- }
+ ASSERT_TRUE(mDecoder->mEos) << "Test Failed. Didn't receive EOS \n";
+
mDecoder->deInitCodec();
int64_t durationUs = extractor->getClipDuration();
- cout << "codec: " << codecName << endl;
+ ALOGV("codec : %s", codecName.c_str());
mDecoder->dumpStatistics(GetParam().first, durationUs);
mDecoder->resetDecoder();
}
@@ -172,6 +145,7 @@
extractor->deInitExtractor();
delete extractor;
delete mDecoder;
+ mDecoder = nullptr;
}
}
@@ -179,26 +153,24 @@
// Add wav files
INSTANTIATE_TEST_SUITE_P(
AudioDecoderTest, C2DecoderTest,
- ::testing::Values(
- make_pair("bbb_44100hz_2ch_128kbps_aac_30sec.mp4", "aac"),
- make_pair("bbb_44100hz_2ch_128kbps_mp3_30sec.mp3", "mp3"),
- make_pair("bbb_8000hz_1ch_8kbps_amrnb_30sec.3gp", "amrnb"),
- make_pair("bbb_16000hz_1ch_9kbps_amrwb_30sec.3gp", "amrnb"),
- make_pair("bbb_44100hz_2ch_80kbps_vorbis_30sec.mp4", "vorbis"),
- make_pair("bbb_44100hz_2ch_600kbps_flac_30sec.mp4", "flac"),
- make_pair("bbb_48000hz_2ch_100kbps_opus_30sec.webm", "opus")));
+ ::testing::Values(make_pair("bbb_44100hz_2ch_128kbps_aac_30sec.mp4", "aac"),
+ make_pair("bbb_44100hz_2ch_128kbps_mp3_30sec.mp3", "mp3"),
+ make_pair("bbb_8000hz_1ch_8kbps_amrnb_30sec.3gp", "amrnb"),
+ make_pair("bbb_16000hz_1ch_9kbps_amrwb_30sec.3gp", "amrnb"),
+ make_pair("bbb_44100hz_2ch_80kbps_vorbis_30sec.mp4", "vorbis"),
+ make_pair("bbb_44100hz_2ch_600kbps_flac_30sec.mp4", "flac"),
+ make_pair("bbb_48000hz_2ch_100kbps_opus_30sec.webm", "opus")));
INSTANTIATE_TEST_SUITE_P(
VideoDecoderTest, C2DecoderTest,
- ::testing::Values(
- make_pair("crowd_1920x1080_25fps_4000kbps_vp9.webm", "vp9"),
- make_pair("crowd_1920x1080_25fps_4000kbps_vp8.webm", "vp8"),
- make_pair("crowd_1920x1080_25fps_4000kbps_av1.webm", "av1"),
- make_pair("crowd_1920x1080_25fps_7300kbps_mpeg2.mp4", "mpeg2"),
- make_pair("crowd_1920x1080_25fps_6000kbps_mpeg4.mp4", "mpeg4"),
- make_pair("crowd_352x288_25fps_6000kbps_h263.3gp", "h263"),
- make_pair("crowd_1920x1080_25fps_6700kbps_h264.ts", "avc"),
- make_pair("crowd_1920x1080_25fps_4000kbps_h265.mkv", "hevc")));
+ ::testing::Values(make_pair("crowd_1920x1080_25fps_4000kbps_vp9.webm", "vp9"),
+ make_pair("crowd_1920x1080_25fps_4000kbps_vp8.webm", "vp8"),
+ make_pair("crowd_1920x1080_25fps_4000kbps_av1.webm", "av1"),
+ make_pair("crowd_1920x1080_25fps_7300kbps_mpeg2.mp4", "mpeg2"),
+ make_pair("crowd_1920x1080_25fps_6000kbps_mpeg4.mp4", "mpeg4"),
+ make_pair("crowd_352x288_25fps_6000kbps_h263.3gp", "h263"),
+ make_pair("crowd_1920x1080_25fps_6700kbps_h264.ts", "avc"),
+ make_pair("crowd_1920x1080_25fps_4000kbps_h265.mkv", "hevc")));
int main(int argc, char **argv) {
gEnv = new BenchmarkTestEnvironment();
diff --git a/media/tests/benchmark/tests/C2EncoderTest.cpp b/media/tests/benchmark/tests/C2EncoderTest.cpp
index 7eb5ff2..98eb17a 100644
--- a/media/tests/benchmark/tests/C2EncoderTest.cpp
+++ b/media/tests/benchmark/tests/C2EncoderTest.cpp
@@ -17,7 +17,6 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "C2EncoderTest"
-#include <sys/stat.h>
#include <fstream>
#include <iostream>
#include <limits>
@@ -30,7 +29,19 @@
class C2EncoderTest : public ::testing::TestWithParam<pair<string, string>> {
public:
- C2EncoderTest() : mEncoder(nullptr) { setupC2EncoderTest(); }
+ C2EncoderTest() : mEncoder(nullptr) {}
+
+ ~C2EncoderTest() {
+ if (!mCodecList.empty()) {
+ mCodecList.clear();
+ }
+ if (mEncoder) {
+ delete mEncoder;
+ mEncoder = nullptr;
+ }
+ }
+
+ virtual void SetUp() override { setupC2EncoderTest(); }
void setupC2EncoderTest();
@@ -128,7 +139,7 @@
mEncoder->deInitCodec();
int64_t durationUs = extractor->getClipDuration();
- cout << "codec: " << codecName << endl;
+ ALOGV("codec : %s", codecName.c_str());
mEncoder->dumpStatistics(GetParam().first, durationUs);
mEncoder->resetEncoder();
}
@@ -143,26 +154,25 @@
extractor->deInitExtractor();
delete decoder;
delete mEncoder;
+ mEncoder = nullptr;
}
INSTANTIATE_TEST_SUITE_P(
AudioEncoderTest, C2EncoderTest,
- ::testing::Values(
- make_pair("bbb_44100hz_2ch_128kbps_aac_30sec.mp4", "aac"),
- make_pair("bbb_8000hz_1ch_8kbps_amrnb_30sec.3gp", "amrnb"),
- make_pair("bbb_16000hz_1ch_9kbps_amrwb_30sec.3gp", "amrwb"),
- make_pair("bbb_44100hz_2ch_600kbps_flac_30sec.mp4", "flac"),
- make_pair("bbb_48000hz_2ch_100kbps_opus_30sec.webm", "opus")));
+ ::testing::Values(make_pair("bbb_44100hz_2ch_128kbps_aac_30sec.mp4", "aac"),
+ make_pair("bbb_8000hz_1ch_8kbps_amrnb_30sec.3gp", "amrnb"),
+ make_pair("bbb_16000hz_1ch_9kbps_amrwb_30sec.3gp", "amrwb"),
+ make_pair("bbb_44100hz_2ch_600kbps_flac_30sec.mp4", "flac"),
+ make_pair("bbb_48000hz_2ch_100kbps_opus_30sec.webm", "opus")));
INSTANTIATE_TEST_SUITE_P(
VideoEncoderTest, C2EncoderTest,
- ::testing::Values(
- make_pair("crowd_1920x1080_25fps_4000kbps_vp9.webm", "vp9"),
- make_pair("crowd_1920x1080_25fps_4000kbps_vp8.webm", "vp8"),
- make_pair("crowd_176x144_25fps_6000kbps_mpeg4.mp4", "mpeg4"),
- make_pair("crowd_176x144_25fps_6000kbps_h263.3gp", "h263"),
- make_pair("crowd_1920x1080_25fps_6700kbps_h264.ts", "avc"),
- make_pair("crowd_1920x1080_25fps_4000kbps_h265.mkv", "hevc")));
+ ::testing::Values(make_pair("crowd_1920x1080_25fps_4000kbps_vp9.webm", "vp9"),
+ make_pair("crowd_1920x1080_25fps_4000kbps_vp8.webm", "vp8"),
+ make_pair("crowd_176x144_25fps_6000kbps_mpeg4.mp4", "mpeg4"),
+ make_pair("crowd_176x144_25fps_6000kbps_h263.3gp", "h263"),
+ make_pair("crowd_1920x1080_25fps_6700kbps_h264.ts", "avc"),
+ make_pair("crowd_1920x1080_25fps_4000kbps_h265.mkv", "hevc")));
int main(int argc, char **argv) {
gEnv = new BenchmarkTestEnvironment();
diff --git a/media/tests/benchmark/tests/DecoderTest.cpp b/media/tests/benchmark/tests/DecoderTest.cpp
index fa37435..5c6aa5b 100644
--- a/media/tests/benchmark/tests/DecoderTest.cpp
+++ b/media/tests/benchmark/tests/DecoderTest.cpp
@@ -21,8 +21,8 @@
#include <iostream>
#include <limits>
-#include "Decoder.h"
#include "BenchmarkTestEnvironment.h"
+#include "Decoder.h"
static BenchmarkTestEnvironment *gEnv = nullptr;
@@ -34,41 +34,30 @@
string inputFile = gEnv->getRes() + get<0>(params);
FILE *inputFp = fopen(inputFile.c_str(), "rb");
- if (!inputFp) {
- cout << "[ WARN ] Test Skipped. Unable to open input file for reading \n";
- return;
- }
+ ASSERT_NE(inputFp, nullptr) << "Unable to open " << inputFile << " file for reading";
Decoder *decoder = new Decoder();
+ ASSERT_NE(decoder, nullptr) << "Decoder creation failed";
+
Extractor *extractor = decoder->getExtractor();
- if (!extractor) {
- cout << "[ WARN ] Test Skipped. Extractor creation failed \n";
- return;
- }
+ ASSERT_NE(extractor, nullptr) << "Extractor creation failed";
// Read file properties
- fseek(inputFp, 0, SEEK_END);
- size_t fileSize = ftell(inputFp);
- fseek(inputFp, 0, SEEK_SET);
+ struct stat buf;
+ stat(inputFile.c_str(), &buf);
+ size_t fileSize = buf.st_size;
int32_t fd = fileno(inputFp);
int32_t trackCount = extractor->initExtractor(fd, fileSize);
- if (trackCount <= 0) {
- cout << "[ WARN ] Test Skipped. initExtractor failed\n";
- return;
- }
+ ASSERT_GT(trackCount, 0) << "initExtractor failed";
+
for (int curTrack = 0; curTrack < trackCount; curTrack++) {
int32_t status = extractor->setupTrackFormat(curTrack);
- if (status != 0) {
- cout << "[ WARN ] Test Skipped. Track Format invalid \n";
- return;
- }
+ ASSERT_EQ(status, 0) << "Track Format invalid";
uint8_t *inputBuffer = (uint8_t *)malloc(kMaxBufferSize);
- if (!inputBuffer) {
- cout << "[ WARN ] Test Skipped. Insufficient memory \n";
- return;
- }
+ ASSERT_NE(inputBuffer, nullptr) << "Insufficient memory";
+
vector<AMediaCodecBufferInfo> frameInfo;
AMediaCodecBufferInfo info;
uint32_t inputBufferOffset = 0;
@@ -78,11 +67,9 @@
status = extractor->getFrameSample(info);
if (status || !info.size) break;
// copy the meta data and buffer to be passed to decoder
- if (inputBufferOffset + info.size > kMaxBufferSize) {
- cout << "[ WARN ] Test Skipped. Memory allocated not sufficient\n";
- free(inputBuffer);
- return;
- }
+ ASSERT_LE(inputBufferOffset + info.size, kMaxBufferSize)
+ << "Memory allocated not sufficient";
+
memcpy(inputBuffer + inputBufferOffset, extractor->getFrameBuf(), info.size);
frameInfo.push_back(info);
inputBufferOffset += info.size;
@@ -92,13 +79,10 @@
bool asyncMode = get<2>(params);
decoder->setupDecoder();
status = decoder->decode(inputBuffer, frameInfo, codecName, asyncMode);
- if (status != AMEDIA_OK) {
- cout << "[ WARN ] Test Failed. Decode returned error " << status << endl;
- free(inputBuffer);
- return;
- }
+ ASSERT_EQ(status, AMEDIA_OK) << "Decoder failed for " << codecName;
+
decoder->deInitCodec();
- cout << "codec : " << codecName << endl;
+ ALOGV("codec : %s", codecName.c_str());
string inputReference = get<0>(params);
decoder->dumpStatistics(inputReference);
free(inputBuffer);
diff --git a/media/tests/benchmark/tests/EncoderTest.cpp b/media/tests/benchmark/tests/EncoderTest.cpp
index c3963f8..dc2a2dd 100644
--- a/media/tests/benchmark/tests/EncoderTest.cpp
+++ b/media/tests/benchmark/tests/EncoderTest.cpp
@@ -20,8 +20,8 @@
#include <fstream>
#include "BenchmarkTestEnvironment.h"
-#include "Encoder.h"
#include "Decoder.h"
+#include "Encoder.h"
static BenchmarkTestEnvironment *gEnv = nullptr;
@@ -33,42 +33,33 @@
string inputFile = gEnv->getRes() + get<0>(params);
FILE *inputFp = fopen(inputFile.c_str(), "rb");
- if (!inputFp) {
- cout << "[ WARN ] Test Skipped. Unable to open input file for reading \n";
- return;
- }
+ ASSERT_NE(inputFp, nullptr) << "Unable to open " << inputFile << " file for reading";
Decoder *decoder = new Decoder();
+ ASSERT_NE(decoder, nullptr) << "Decoder creation failed";
+
Extractor *extractor = decoder->getExtractor();
- if (!extractor) {
- cout << "[ WARN ] Test Skipped. Extractor creation failed \n";
- return;
- }
+ ASSERT_NE(extractor, nullptr) << "Extractor creation failed";
+
// Read file properties
- fseek(inputFp, 0, SEEK_END);
- size_t fileSize = ftell(inputFp);
- fseek(inputFp, 0, SEEK_SET);
+ struct stat buf;
+ stat(inputFile.c_str(), &buf);
+ size_t fileSize = buf.st_size;
int32_t fd = fileno(inputFp);
int32_t trackCount = extractor->initExtractor(fd, fileSize);
- if (trackCount <= 0) {
- cout << "[ WARN ] Test Skipped. initExtractor failed\n";
- return;
- }
+ ASSERT_GT(trackCount, 0) << "initExtractor failed";
Encoder *encoder = new Encoder();
+ ASSERT_NE(encoder, nullptr) << "Decoder creation failed";
+
for (int curTrack = 0; curTrack < trackCount; curTrack++) {
int32_t status = extractor->setupTrackFormat(curTrack);
- if (status != 0) {
- cout << "[ WARN ] Test Skipped. Track Format invalid \n";
- return;
- }
+ ASSERT_EQ(status, 0) << "Track Format invalid";
uint8_t *inputBuffer = (uint8_t *)malloc(kMaxBufferSize);
- if (!inputBuffer) {
- cout << "[ WARN ] Test Skipped. Insufficient memory \n";
- return;
- }
+ ASSERT_NE(inputBuffer, nullptr) << "Insufficient memory";
+
vector<AMediaCodecBufferInfo> frameInfo;
AMediaCodecBufferInfo info;
uint32_t inputBufferOffset = 0;
@@ -78,11 +69,9 @@
status = extractor->getFrameSample(info);
if (status || !info.size) break;
// copy the meta data and buffer to be passed to decoder
- if (inputBufferOffset + info.size > kMaxBufferSize) {
- cout << "[ WARN ] Test Skipped. Memory allocated not sufficient\n";
- free(inputBuffer);
- return;
- }
+ ASSERT_LE(inputBufferOffset + info.size, kMaxBufferSize)
+ << "Memory allocated not sufficient";
+
memcpy(inputBuffer + inputBufferOffset, extractor->getFrameBuf(), info.size);
frameInfo.push_back(info);
inputBufferOffset += info.size;
@@ -91,16 +80,12 @@
string decName = "";
string outputFileName = "decode.out";
FILE *outFp = fopen(outputFileName.c_str(), "wb");
- if (outFp == nullptr) {
- ALOGE("Unable to open output file for writing");
- return;
- }
+ ASSERT_NE(outFp, nullptr) << "Unable to open output file" << outputFileName
+ << " for dumping decoder's output";
+
decoder->setupDecoder();
status = decoder->decode(inputBuffer, frameInfo, decName, false /*asyncMode */, outFp);
- if (status != AMEDIA_OK) {
- cout << "[ WARN ] Test Skipped. Decode returned error \n";
- return;
- }
+ ASSERT_EQ(status, AMEDIA_OK) << "Decode returned error : " << status;
ifstream eleStream;
eleStream.open(outputFileName.c_str(), ifstream::binary | ifstream::ate);
@@ -111,15 +96,13 @@
AMediaFormat *format = extractor->getFormat();
const char *mime = nullptr;
AMediaFormat_getString(format, AMEDIAFORMAT_KEY_MIME, &mime);
- if (!mime) {
- ALOGE("Error in AMediaFormat_getString");
- return;
- }
+ ASSERT_NE(mime, nullptr) << "Invalid mime type";
+
// Get encoder params
encParameter encParams;
if (!strncmp(mime, "video/", 6)) {
- AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_WIDTH, &encParams.width);
- AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_HEIGHT, &encParams.height);
+ ASSERT_TRUE(AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_WIDTH, &encParams.width));
+ ASSERT_TRUE(AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_HEIGHT, &encParams.height));
AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_FRAME_RATE, &encParams.frameRate);
AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_BIT_RATE, &encParams.bitrate);
if (encParams.bitrate <= 0 || encParams.frameRate <= 0) {
@@ -133,8 +116,10 @@
AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_PROFILE, &encParams.profile);
AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_LEVEL, &encParams.level);
} else {
- AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_SAMPLE_RATE, &encParams.sampleRate);
- AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_CHANNEL_COUNT, &encParams.numChannels);
+ ASSERT_TRUE(AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_SAMPLE_RATE,
+ &encParams.sampleRate));
+ ASSERT_TRUE(AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_CHANNEL_COUNT,
+ &encParams.numChannels));
encParams.bitrate =
encParams.sampleRate * encParams.numChannels * 16 /* bitsPerSample */;
}
@@ -143,13 +128,10 @@
string codecName = get<1>(params);
bool asyncMode = get<2>(params);
status = encoder->encode(codecName, eleStream, eleSize, asyncMode, encParams, (char *)mime);
- if (status != AMEDIA_OK) {
- cout << "[ WARN ] Test Failed. Encode returned error " << status << endl;
- free(inputBuffer);
- return;
- }
+ ASSERT_EQ(status, 0) << "Encoder failed for " << codecName;
+
encoder->deInitCodec();
- cout << "codec : " << codecName << endl;
+ ALOGV("codec : %s", codecName.c_str());
string inputReference = get<0>(params);
encoder->dumpStatistics(inputReference, extractor->getClipDuration());
eleStream.close();
diff --git a/media/tests/benchmark/tests/ExtractorTest.cpp b/media/tests/benchmark/tests/ExtractorTest.cpp
index dd0d711..c2d72ff 100644
--- a/media/tests/benchmark/tests/ExtractorTest.cpp
+++ b/media/tests/benchmark/tests/ExtractorTest.cpp
@@ -19,8 +19,8 @@
#include <gtest/gtest.h>
-#include "Extractor.h"
#include "BenchmarkTestEnvironment.h"
+#include "Extractor.h"
static BenchmarkTestEnvironment *gEnv = nullptr;
@@ -28,33 +28,24 @@
TEST_P(ExtractorTest, Extract) {
Extractor *extractObj = new Extractor();
+ ASSERT_NE(extractObj, nullptr) << "Extractor creation failed";
string inputFile = gEnv->getRes() + GetParam().first;
FILE *inputFp = fopen(inputFile.c_str(), "rb");
- if (!inputFp) {
- cout << "[ WARN ] Test Skipped. Unable to open input file for reading \n";
- return;
- }
+ ASSERT_NE(inputFp, nullptr) << "Unable to open " << inputFile << " file for reading";
// Read file properties
- size_t fileSize = 0;
- fseek(inputFp, 0, SEEK_END);
- fileSize = ftell(inputFp);
- fseek(inputFp, 0, SEEK_SET);
+ struct stat buf;
+ stat(inputFile.c_str(), &buf);
+ size_t fileSize = buf.st_size;
int32_t fd = fileno(inputFp);
int32_t trackCount = extractObj->initExtractor(fd, fileSize);
- if (trackCount <= 0) {
- cout << "[ WARN ] Test Skipped. initExtractor failed\n";
- return;
- }
+ ASSERT_GT(trackCount, 0) << "initExtractor failed";
int32_t trackID = GetParam().second;
int32_t status = extractObj->extract(trackID);
- if (status != AMEDIA_OK) {
- cout << "[ WARN ] Test Skipped. Extraction failed \n";
- return;
- }
+ ASSERT_EQ(status, AMEDIA_OK) << "Extraction failed \n";
extractObj->deInitExtractor();
@@ -79,7 +70,8 @@
make_pair("bbb_8000hz_1ch_8kbps_amrnb_5mins.3gp", 0),
make_pair("bbb_16000hz_1ch_9kbps_amrwb_5mins.3gp", 0),
make_pair("bbb_44100hz_2ch_80kbps_vorbis_5mins.mp4", 0),
- make_pair("bbb_48000hz_2ch_100kbps_opus_5mins.webm", 0)));
+ make_pair("bbb_48000hz_2ch_100kbps_opus_5mins.webm",
+ 0)));
int main(int argc, char **argv) {
gEnv = new BenchmarkTestEnvironment();
diff --git a/media/tests/benchmark/tests/MuxerTest.cpp b/media/tests/benchmark/tests/MuxerTest.cpp
index e814f90..7b01732 100644
--- a/media/tests/benchmark/tests/MuxerTest.cpp
+++ b/media/tests/benchmark/tests/MuxerTest.cpp
@@ -21,8 +21,8 @@
#include <fstream>
#include <iostream>
-#include "Muxer.h"
#include "BenchmarkTestEnvironment.h"
+#include "Muxer.h"
#define OUTPUT_FILE_NAME "/data/local/tmp/mux.out"
@@ -53,49 +53,34 @@
ALOGV("Mux the samples given by extractor");
string inputFile = gEnv->getRes() + GetParam().first;
FILE *inputFp = fopen(inputFile.c_str(), "rb");
- if (!inputFp) {
- cout << "[ WARN ] Test Skipped. Unable to open input file for reading \n";
- return;
- }
+ ASSERT_NE(inputFp, nullptr) << "Unable to open " << inputFile << " file for reading";
+
string fmt = GetParam().second;
MUXER_OUTPUT_T outputFormat = getMuxerOutFormat(fmt);
- if (outputFormat == MUXER_OUTPUT_FORMAT_INVALID) {
- ALOGE("output format is MUXER_OUTPUT_FORMAT_INVALID");
- return;
- }
+ ASSERT_NE(outputFormat, MUXER_OUTPUT_FORMAT_INVALID) << "Invalid muxer output format";
Muxer *muxerObj = new Muxer();
+ ASSERT_NE(muxerObj, nullptr) << "Muxer creation failed";
+
Extractor *extractor = muxerObj->getExtractor();
- if (!extractor) {
- cout << "[ WARN ] Test Skipped. Extractor creation failed \n";
- return;
- }
+ ASSERT_NE(extractor, nullptr) << "Extractor creation failed";
// Read file properties
- size_t fileSize = 0;
- fseek(inputFp, 0, SEEK_END);
- fileSize = ftell(inputFp);
- fseek(inputFp, 0, SEEK_SET);
+ struct stat buf;
+ stat(inputFile.c_str(), &buf);
+ size_t fileSize = buf.st_size;
int32_t fd = fileno(inputFp);
int32_t trackCount = extractor->initExtractor(fd, fileSize);
- if (trackCount <= 0) {
- cout << "[ WARN ] Test Skipped. initExtractor failed\n";
- return;
- }
+ ASSERT_GT(trackCount, 0) << "initExtractor failed";
for (int curTrack = 0; curTrack < trackCount; curTrack++) {
int32_t status = extractor->setupTrackFormat(curTrack);
- if (status != 0) {
- cout << "[ WARN ] Test Skipped. Track Format invalid \n";
- return;
- }
+ ASSERT_EQ(status, 0) << "Track Format invalid";
uint8_t *inputBuffer = (uint8_t *)malloc(kMaxBufferSize);
- if (!inputBuffer) {
- std::cout << "[ WARN ] Test Skipped. Insufficient memory \n";
- return;
- }
+ ASSERT_NE(inputBuffer, nullptr) << "Insufficient memory";
+
// AMediaCodecBufferInfo : <size of frame> <flags> <presentationTimeUs> <offset>
vector<AMediaCodecBufferInfo> frameInfos;
AMediaCodecBufferInfo info;
@@ -106,11 +91,9 @@
status = extractor->getFrameSample(info);
if (status || !info.size) break;
// copy the meta data and buffer to be passed to muxer
- if (inputBufferOffset + info.size > kMaxBufferSize) {
- cout << "[ WARN ] Test Skipped. Memory allocated not sufficient\n";
- free(inputBuffer);
- return;
- }
+ ASSERT_LE(inputBufferOffset + info.size, kMaxBufferSize)
+ << "Memory allocated not sufficient";
+
memcpy(inputBuffer + inputBufferOffset, extractor->getFrameBuf(), info.size);
info.offset = inputBufferOffset;
frameInfos.push_back(info);
@@ -119,22 +102,16 @@
string outputFileName = OUTPUT_FILE_NAME;
FILE *outputFp = fopen(outputFileName.c_str(), "w+b");
- if (!outputFp) {
- cout << "[ WARN ] Test Skipped. Unable to open output file for writing \n";
- return;
- }
+ ASSERT_NE(outputFp, nullptr)
+ << "Unable to open output file" << outputFileName << " for writing";
+
int32_t fd = fileno(outputFp);
status = muxerObj->initMuxer(fd, outputFormat);
- if (status != 0) {
- cout << "[ WARN ] Test Skipped. initMuxer failed\n";
- return;
- }
+ ASSERT_EQ(status, 0) << "initMuxer failed";
status = muxerObj->mux(inputBuffer, frameInfos);
- if (status != 0) {
- cout << "[ WARN ] Test Skipped. Mux failed \n";
- return;
- }
+ ASSERT_EQ(status, 0) << "Mux failed";
+
muxerObj->deInitMuxer();
muxerObj->dumpStatistics(GetParam().first + "." + fmt.c_str());
free(inputBuffer);
diff --git a/media/utils/Android.bp b/media/utils/Android.bp
index 5047b19..b7037e9 100644
--- a/media/utils/Android.bp
+++ b/media/utils/Android.bp
@@ -29,6 +29,7 @@
"libc_malloc_debug_backtrace",
],
shared_libs: [
+ "libaudioutils", // for clock.h
"libbinder",
"libcutils",
"liblog",
diff --git a/media/utils/ServiceUtilities.cpp b/media/utils/ServiceUtilities.cpp
index a77311e..a33ed97 100644
--- a/media/utils/ServiceUtilities.cpp
+++ b/media/utils/ServiceUtilities.cpp
@@ -16,6 +16,7 @@
#define LOG_TAG "ServiceUtilities"
+#include <audio_utils/clock.h>
#include <binder/AppOpsManager.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
@@ -24,6 +25,7 @@
#include <iterator>
#include <algorithm>
+#include <pwd.h>
/* When performing permission checks we do not use permission cache for
* runtime permissions (protection level dangerous) as they may change at
@@ -329,4 +331,128 @@
}
}
+// How long we hold info before we re-fetch it (24 hours) if we found it previously.
+static constexpr nsecs_t INFO_EXPIRATION_NS = 24 * 60 * 60 * NANOS_PER_SECOND;
+// Maximum info records we retain before clearing everything.
+static constexpr size_t INFO_CACHE_MAX = 1000;
+
+// The original code is from MediaMetricsService.cpp.
+mediautils::UidInfo::Info mediautils::UidInfo::getInfo(uid_t uid)
+{
+ const nsecs_t now = systemTime(SYSTEM_TIME_REALTIME);
+ struct mediautils::UidInfo::Info info;
+ {
+ std::lock_guard _l(mLock);
+ auto it = mInfoMap.find(uid);
+ if (it != mInfoMap.end()) {
+ info = it->second;
+ ALOGV("%s: uid %d expiration %lld now %lld",
+ __func__, uid, (long long)info.expirationNs, (long long)now);
+ if (info.expirationNs <= now) {
+ // purge the stale entry and fall into re-fetching
+ ALOGV("%s: entry for uid %d expired, now %lld",
+ __func__, uid, (long long)now);
+ mInfoMap.erase(it);
+ info.uid = (uid_t)-1; // this is always fully overwritten
+ }
+ }
+ }
+
+ // if we did not find it in our map, look it up
+ if (info.uid == (uid_t)(-1)) {
+ sp<IServiceManager> sm = defaultServiceManager();
+ sp<content::pm::IPackageManagerNative> package_mgr;
+ if (sm.get() == nullptr) {
+ ALOGE("%s: Cannot find service manager", __func__);
+ } else {
+ sp<IBinder> binder = sm->getService(String16("package_native"));
+ if (binder.get() == nullptr) {
+ ALOGE("%s: Cannot find package_native", __func__);
+ } else {
+ package_mgr = interface_cast<content::pm::IPackageManagerNative>(binder);
+ }
+ }
+
+ // find package name
+ std::string pkg;
+ if (package_mgr != nullptr) {
+ std::vector<std::string> names;
+ binder::Status status = package_mgr->getNamesForUids({(int)uid}, &names);
+ if (!status.isOk()) {
+ ALOGE("%s: getNamesForUids failed: %s",
+ __func__, status.exceptionMessage().c_str());
+ } else {
+ if (!names[0].empty()) {
+ pkg = names[0].c_str();
+ }
+ }
+ }
+
+ if (pkg.empty()) {
+ struct passwd pw{}, *result;
+ char buf[8192]; // extra buffer space - should exceed what is
+ // required in struct passwd_pw (tested),
+ // and even then this is only used in backup
+ // when the package manager is unavailable.
+ if (getpwuid_r(uid, &pw, buf, sizeof(buf), &result) == 0
+ && result != nullptr
+ && result->pw_name != nullptr) {
+ pkg = result->pw_name;
+ }
+ }
+
+ // strip any leading "shared:" strings that came back
+ if (pkg.compare(0, 7, "shared:") == 0) {
+ pkg.erase(0, 7);
+ }
+
+ // determine how pkg was installed and the versionCode
+ std::string installer;
+ int64_t versionCode = 0;
+ bool notFound = false;
+ if (pkg.empty()) {
+ pkg = std::to_string(uid); // not found
+ notFound = true;
+ } else if (strchr(pkg.c_str(), '.') == nullptr) {
+ // not of form 'com.whatever...'; assume internal
+ // so we don't need to look it up in package manager.
+ } else if (package_mgr.get() != nullptr) {
+ String16 pkgName16(pkg.c_str());
+ binder::Status status = package_mgr->getInstallerForPackage(pkgName16, &installer);
+ if (!status.isOk()) {
+ ALOGE("%s: getInstallerForPackage failed: %s",
+ __func__, status.exceptionMessage().c_str());
+ }
+
+ // skip if we didn't get an installer
+ if (status.isOk()) {
+ status = package_mgr->getVersionCodeForPackage(pkgName16, &versionCode);
+ if (!status.isOk()) {
+ ALOGE("%s: getVersionCodeForPackage failed: %s",
+ __func__, status.exceptionMessage().c_str());
+ }
+ }
+
+ ALOGV("%s: package '%s' installed by '%s' versioncode %lld",
+ __func__, pkg.c_str(), installer.c_str(), (long long)versionCode);
+ }
+
+ // add it to the map, to save a subsequent lookup
+ std::lock_guard _l(mLock);
+ // first clear if we have too many cached elements. This would be rare.
+ if (mInfoMap.size() >= INFO_CACHE_MAX) mInfoMap.clear();
+
+ // always overwrite
+ info.uid = uid;
+ info.package = std::move(pkg);
+ info.installer = std::move(installer);
+ info.versionCode = versionCode;
+ info.expirationNs = now + (notFound ? 0 : INFO_EXPIRATION_NS);
+ ALOGV("%s: adding uid %d package '%s' expirationNs: %lld",
+ __func__, uid, info.package.c_str(), (long long)info.expirationNs);
+ mInfoMap[uid] = info;
+ }
+ return info;
+}
+
} // namespace android
diff --git a/media/utils/include/mediautils/ServiceUtilities.h b/media/utils/include/mediautils/ServiceUtilities.h
index e467058..4925cdb 100644
--- a/media/utils/include/mediautils/ServiceUtilities.h
+++ b/media/utils/include/mediautils/ServiceUtilities.h
@@ -28,6 +28,7 @@
#include <map>
#include <optional>
#include <string>
+#include <unordered_map>
#include <vector>
namespace android {
@@ -118,6 +119,40 @@
using Packages = std::vector<Package>;
std::map<uid_t, Packages> mDebugLog;
};
-}
+
+namespace mediautils {
+
+/**
+ * This class is used to retrieve (and cache) package information
+ * for a given uid.
+ */
+class UidInfo {
+public:
+ struct Info {
+ uid_t uid = -1; // uid used for lookup.
+ std::string package; // package name.
+ std::string installer; // installer for the package (e.g. preload, play store).
+ int64_t versionCode = 0; // reported version code.
+ int64_t expirationNs = 0; // after this time in SYSTEM_TIME_REALTIME we refetch.
+ };
+
+ /**
+ * Returns the package information for a UID.
+ *
+ * The package name will be the uid if we cannot find the associated name.
+ *
+ * \param uid is the uid of the app or service.
+ */
+ Info getInfo(uid_t uid);
+
+private:
+ std::mutex mLock;
+ // TODO: use concurrent hashmap with striped lock.
+ std::unordered_map<uid_t, Info> mInfoMap; // GUARDED_BY(mLock)
+};
+
+} // namespace mediautils
+
+} // namespace android
#endif // ANDROID_MEDIAUTILS_SERVICEUTILITIES_H
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index d4d59d6..e198beb 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -1369,6 +1369,14 @@
// For encoded streams force direct flag to prevent downstream mixing.
sinkConfig->flags.output = static_cast<audio_output_flags_t>(
sinkConfig->flags.output | AUDIO_OUTPUT_FLAG_DIRECT);
+ if (audio_is_iec61937_compatible(sinkConfig->format)) {
+ // For formats compatible with IEC61937 encapsulation, assume that
+ // the record thread input from MSD is IEC61937 framed (for proportional buffer sizing).
+ // Add the AUDIO_OUTPUT_FLAG_IEC958_NONAUDIO flag so downstream HAL can distinguish between
+ // raw and IEC61937 framed streams.
+ sinkConfig->flags.output = static_cast<audio_output_flags_t>(
+ sinkConfig->flags.output | AUDIO_OUTPUT_FLAG_IEC958_NONAUDIO);
+ }
sourceConfig->sample_rate = bestSinkConfig.sample_rate;
// Specify exact channel mask to prevent guessing by bit count in PatchPanel.
sourceConfig->channel_mask = audio_channel_mask_out_to_in(bestSinkConfig.channel_mask);
diff --git a/services/camera/libcameraservice/api1/client2/Parameters.cpp b/services/camera/libcameraservice/api1/client2/Parameters.cpp
index 18addb5..20333d1 100644
--- a/services/camera/libcameraservice/api1/client2/Parameters.cpp
+++ b/services/camera/libcameraservice/api1/client2/Parameters.cpp
@@ -2454,12 +2454,9 @@
camera_metadata_ro_entry_t availableFocalLengths =
staticInfo(ANDROID_LENS_INFO_AVAILABLE_FOCAL_LENGTHS, 0, 0, /*required*/false);
- if (!availableFocalLengths.count && !fastInfo.isExternalCamera) return NO_INIT;
// Find focal length in PREVIEW template to use as default focal length.
- if (fastInfo.isExternalCamera) {
- fastInfo.defaultFocalLength = -1.0;
- } else {
+ if (availableFocalLengths.count) {
// Find smallest (widest-angle) focal length to use as basis of still
// picture FOV reporting.
fastInfo.defaultFocalLength = availableFocalLengths.data.f[0];
@@ -2481,6 +2478,10 @@
if (entry.count != 0) {
fastInfo.defaultFocalLength = entry.data.f[0];
}
+ } else if (fastInfo.isExternalCamera) {
+ fastInfo.defaultFocalLength = -1.0;
+ } else {
+ return NO_INIT;
}
return OK;
}
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.cpp b/services/camera/libcameraservice/common/CameraProviderManager.cpp
index c8c31f3..0f74a48 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.cpp
+++ b/services/camera/libcameraservice/common/CameraProviderManager.cpp
@@ -2082,6 +2082,13 @@
CameraProviderManager::statusToString(status), status);
return;
}
+
+ res = camera3::ZoomRatioMapper::overrideZoomRatioTags(
+ &mPhysicalCameraCharacteristics[id], &mSupportNativeZoomRatio);
+ if (OK != res) {
+ ALOGE("%s: Unable to override zoomRatio related tags: %s (%d)",
+ __FUNCTION__, strerror(-res), res);
+ }
}
}
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index ccd1776..92ba5e4 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -161,14 +161,9 @@
}
}
- res = mZoomRatioMappers[physicalId].initZoomRatioTags(
+ mZoomRatioMappers[physicalId] = ZoomRatioMapper(
&mPhysicalDeviceInfoMap[physicalId],
mSupportNativeZoomRatio, usePrecorrectArray);
- if (res != OK) {
- SET_ERR_L("Failed to initialize camera %s's zoomRatio tags: %s (%d)",
- physicalId.c_str(), strerror(-res), res);
- return res;
- }
}
}
@@ -353,13 +348,8 @@
}
}
- res = mZoomRatioMappers[mId.c_str()].initZoomRatioTags(&mDeviceInfo,
+ mZoomRatioMappers[mId.c_str()] = ZoomRatioMapper(&mDeviceInfo,
mSupportNativeZoomRatio, usePrecorrectArray);
- if (res != OK) {
- SET_ERR_L("Failed to initialize zoomRatio tags: %s (%d)",
- strerror(-res), res);
- return res;
- }
return OK;
}
diff --git a/services/camera/libcameraservice/device3/ZoomRatioMapper.cpp b/services/camera/libcameraservice/device3/ZoomRatioMapper.cpp
index 7718819..84da45a 100644
--- a/services/camera/libcameraservice/device3/ZoomRatioMapper.cpp
+++ b/services/camera/libcameraservice/device3/ZoomRatioMapper.cpp
@@ -25,8 +25,6 @@
namespace camera3 {
-ZoomRatioMapper::ZoomRatioMapper() : mHalSupportsZoomRatio(false) {
-}
status_t ZoomRatioMapper::initZoomRatioInTemplate(CameraMetadata *request) {
camera_metadata_entry_t entry;
@@ -117,19 +115,17 @@
return OK;
}
-status_t ZoomRatioMapper::initZoomRatioTags(const CameraMetadata* deviceInfo,
+ZoomRatioMapper::ZoomRatioMapper(const CameraMetadata* deviceInfo,
bool supportNativeZoomRatio, bool usePrecorrectArray) {
- std::lock_guard<std::mutex> lock(mMutex);
-
camera_metadata_ro_entry_t entry;
entry = deviceInfo->find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
- if (entry.count != 4) return BAD_VALUE;
+ if (entry.count != 4) return;
int32_t arrayW = entry.data.i32[2];
int32_t arrayH = entry.data.i32[3];
entry = deviceInfo->find(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE);
- if (entry.count != 4) return BAD_VALUE;
+ if (entry.count != 4) return;
int32_t activeW = entry.data.i32[2];
int32_t activeH = entry.data.i32[3];
@@ -144,11 +140,12 @@
ALOGV("%s: array size: %d x %d, mHalSupportsZoomRatio %d",
__FUNCTION__, mArrayWidth, mArrayHeight, mHalSupportsZoomRatio);
- return OK;
+ mIsValid = true;
}
status_t ZoomRatioMapper::updateCaptureRequest(CameraMetadata* request) {
- std::lock_guard<std::mutex> lock(mMutex);
+ if (!mIsValid) return INVALID_OPERATION;
+
status_t res = OK;
bool zoomRatioIs1 = true;
camera_metadata_entry_t entry;
@@ -174,7 +171,8 @@
}
status_t ZoomRatioMapper::updateCaptureResult(CameraMetadata* result, bool requestedZoomRatioIs1) {
- std::lock_guard<std::mutex> lock(mMutex);
+ if (!mIsValid) return INVALID_OPERATION;
+
status_t res = OK;
if (mHalSupportsZoomRatio && requestedZoomRatioIs1) {
diff --git a/services/camera/libcameraservice/device3/ZoomRatioMapper.h b/services/camera/libcameraservice/device3/ZoomRatioMapper.h
index 38efbfd..16b223b 100644
--- a/services/camera/libcameraservice/device3/ZoomRatioMapper.h
+++ b/services/camera/libcameraservice/device3/ZoomRatioMapper.h
@@ -19,7 +19,6 @@
#include <utils/Errors.h>
#include <array>
-#include <mutex>
#include "camera/CameraMetadata.h"
#include "device3/CoordinateMapper.h"
@@ -36,7 +35,9 @@
*/
class ZoomRatioMapper : private CoordinateMapper {
public:
- ZoomRatioMapper();
+ ZoomRatioMapper() = default;
+ ZoomRatioMapper(const CameraMetadata *deviceInfo,
+ bool supportNativeZoomRatio, bool usePrecorrectArray);
ZoomRatioMapper(const ZoomRatioMapper& other) :
mHalSupportsZoomRatio(other.mHalSupportsZoomRatio),
mArrayWidth(other.mArrayWidth), mArrayHeight(other.mArrayHeight) {}
@@ -53,16 +54,6 @@
CameraMetadata* deviceInfo, bool* supportNativeZoomRatio);
/**
- * Initialize zoom ratio mapper with static metadata.
- *
- * Note:
- * This function may modify the static metadata with zoomRatio related
- * tags.
- */
- status_t initZoomRatioTags(const CameraMetadata *deviceInfo,
- bool supportNativeZoomRatio, bool usePrecorrectArray);
-
- /**
* Update capture request to handle both cropRegion and zoomRatio.
*/
status_t updateCaptureRequest(CameraMetadata *request);
@@ -82,16 +73,16 @@
void scaleCoordinates(int32_t* coordPairs, int coordCount,
float scaleRatio, ClampMode clamp);
+ bool isValid() { return mIsValid; }
private:
+ // const after construction
bool mHalSupportsZoomRatio;
-
// active array / pre-correction array dimension
int32_t mArrayWidth, mArrayHeight;
- mutable std::mutex mMutex;
+ bool mIsValid = false;
float deriveZoomRatio(const CameraMetadata* metadata);
-
void scaleRects(int32_t* rects, int rectCount, float scaleRatio);
status_t separateZoomFromCropLocked(CameraMetadata* metadata, bool isResult);
diff --git a/services/camera/libcameraservice/tests/ZoomRatioTest.cpp b/services/camera/libcameraservice/tests/ZoomRatioTest.cpp
index 1bfa03f..300da09 100644
--- a/services/camera/libcameraservice/tests/ZoomRatioTest.cpp
+++ b/services/camera/libcameraservice/tests/ZoomRatioTest.cpp
@@ -66,7 +66,8 @@
return res;
}
- return m->initZoomRatioTags(&deviceInfo, hasZoomRatioRange, usePreCorrectArray);
+ *m = ZoomRatioMapper(&deviceInfo, hasZoomRatioRange, usePreCorrectArray);
+ return OK;
}
TEST(ZoomRatioTest, Initialization) {
@@ -86,12 +87,12 @@
res = ZoomRatioMapper::overrideZoomRatioTags(&deviceInfo, &supportNativeZoomRatio);
ASSERT_EQ(res, OK);
ASSERT_EQ(supportNativeZoomRatio, false);
- res = mapperNoZoomRange.initZoomRatioTags(&deviceInfo,
+ mapperNoZoomRange = ZoomRatioMapper(&deviceInfo,
supportNativeZoomRatio, true/*usePreCorrectArray*/);
- ASSERT_EQ(res, OK);
- res = mapperNoZoomRange.initZoomRatioTags(&deviceInfo,
+ ASSERT_TRUE(mapperNoZoomRange.isValid());
+ mapperNoZoomRange = ZoomRatioMapper(&deviceInfo,
supportNativeZoomRatio, false/*usePreCorrectArray*/);
- ASSERT_EQ(res, OK);
+ ASSERT_TRUE(mapperNoZoomRange.isValid());
entry = deviceInfo.find(ANDROID_CONTROL_ZOOM_RATIO_RANGE);
ASSERT_EQ(entry.count, 2U);
@@ -120,12 +121,12 @@
ASSERT_EQ(res, OK);
ASSERT_EQ(supportNativeZoomRatio, true);
ZoomRatioMapper mapperWithZoomRange;
- res = mapperWithZoomRange.initZoomRatioTags(&deviceInfo,
+ mapperWithZoomRange = ZoomRatioMapper(&deviceInfo,
supportNativeZoomRatio, true/*usePreCorrectArray*/);
- ASSERT_EQ(res, OK);
- res = mapperWithZoomRange.initZoomRatioTags(&deviceInfo,
+ ASSERT_TRUE(mapperWithZoomRange.isValid());
+ mapperWithZoomRange = ZoomRatioMapper(&deviceInfo,
supportNativeZoomRatio, false/*usePreCorrectArray*/);
- ASSERT_EQ(res, OK);
+ ASSERT_TRUE(mapperWithZoomRange.isValid());
entry = deviceInfo.find(ANDROID_CONTROL_ZOOM_RATIO_RANGE);
ASSERT_EQ(entry.count, 2U);
diff --git a/services/mediametrics/Android.bp b/services/mediametrics/Android.bp
index 0840f31..20f346c 100644
--- a/services/mediametrics/Android.bp
+++ b/services/mediametrics/Android.bp
@@ -12,6 +12,7 @@
"libbinder",
"liblog",
"libmediametricsservice",
+ "libmediautils",
"libutils",
],
@@ -52,6 +53,7 @@
"libbinder",
"liblog",
"libmediametrics",
+ "libmediautils",
"libprotobuf-cpp-lite",
"libstatslog",
"libutils",
diff --git a/services/mediametrics/MediaMetricsService.cpp b/services/mediametrics/MediaMetricsService.cpp
index bbd13fa..b5bdd6f 100644
--- a/services/mediametrics/MediaMetricsService.cpp
+++ b/services/mediametrics/MediaMetricsService.cpp
@@ -57,6 +57,25 @@
return (timeNs + NANOS_PER_SECOND / 2) / NANOS_PER_SECOND * NANOS_PER_SECOND;
}
+/* static */
+bool MediaMetricsService::useUidForPackage(
+ const std::string& package, const std::string& installer)
+{
+ if (strchr(package.c_str(), '.') == NULL) {
+ return false; // not of form 'com.whatever...'; assume internal and ok
+ } else if (strncmp(package.c_str(), "android.", 8) == 0) {
+ return false; // android.* packages are assumed fine
+ } else if (strncmp(installer.c_str(), "com.android.", 12) == 0) {
+ return false; // from play store
+ } else if (strncmp(installer.c_str(), "com.google.", 11) == 0) {
+ return false; // some google source
+ } else if (strcmp(installer.c_str(), "preload") == 0) {
+ return false; // preloads
+ } else {
+ return true; // we're not sure where it came from, use uid only.
+ }
+}
+
MediaMetricsService::MediaMetricsService()
: mMaxRecords(kMaxRecords),
mMaxRecordAgeNs(kMaxRecordAgeNs),
@@ -112,14 +131,19 @@
break;
}
- // Overwrite package name and version if the caller was untrusted.
- if (!isTrusted) {
- mUidInfo.setPkgInfo(item, item->getUid(), true, true);
- } else if (item->getPkgName().empty()) {
- // empty, so fill out both parts
- mUidInfo.setPkgInfo(item, item->getUid(), true, true);
- } else {
- // trusted, provided a package, do nothing
+ // Overwrite package name and version if the caller was untrusted or empty
+ if (!isTrusted || item->getPkgName().empty()) {
+ const uid_t uid = item->getUid();
+ mediautils::UidInfo::Info info = mUidInfo.getInfo(uid);
+ if (useUidForPackage(info.package, info.installer)) {
+ // remove uid information of unknown installed packages.
+ // TODO: perhaps this can be done just before uploading to Westworld.
+ item->setPkgName(std::to_string(uid));
+ item->setPkgVersionCode(0);
+ } else {
+ item->setPkgName(info.package);
+ item->setPkgVersionCode(info.versionCode);
+ }
}
ALOGV("%s: given uid %d; sanitized uid: %d sanitized pkg: %s "
@@ -450,150 +474,4 @@
return false;
}
-// How long we hold package info before we re-fetch it
-constexpr nsecs_t PKG_EXPIRATION_NS = 30 * 60 * NANOS_PER_SECOND; // 30 minutes
-
-// give me the package name, perhaps going to find it
-// manages its own mutex operations internally
-void MediaMetricsService::UidInfo::setPkgInfo(
- mediametrics::Item *item, uid_t uid, bool setName, bool setVersion)
-{
- ALOGV("%s: uid=%d", __func__, uid);
-
- if (!setName && !setVersion) {
- return; // setting nothing? strange
- }
-
- const nsecs_t now = systemTime(SYSTEM_TIME_REALTIME);
- struct UidToPkgInfo mapping;
- {
- std::lock_guard _l(mUidInfoLock);
- auto it = mPkgMappings.find(uid);
- if (it != mPkgMappings.end()) {
- mapping = it->second;
- ALOGV("%s: uid %d expiration %lld now %lld",
- __func__, uid, (long long)mapping.expiration, (long long)now);
- if (mapping.expiration <= now) {
- // purge the stale entry and fall into re-fetching
- ALOGV("%s: entry for uid %d expired, now %lld",
- __func__, uid, (long long)now);
- mPkgMappings.erase(it);
- mapping.uid = (uid_t)-1; // this is always fully overwritten
- }
- }
- }
-
- // if we did not find it
- if (mapping.uid == (uid_t)(-1)) {
- std::string pkg;
- std::string installer;
- int64_t versionCode = 0;
-
- const struct passwd *pw = getpwuid(uid);
- if (pw) {
- pkg = pw->pw_name;
- }
-
- sp<IServiceManager> sm = defaultServiceManager();
- sp<content::pm::IPackageManagerNative> package_mgr;
- if (sm.get() == nullptr) {
- ALOGE("%s: Cannot find service manager", __func__);
- } else {
- sp<IBinder> binder = sm->getService(String16("package_native"));
- if (binder.get() == nullptr) {
- ALOGE("%s: Cannot find package_native", __func__);
- } else {
- package_mgr = interface_cast<content::pm::IPackageManagerNative>(binder);
- }
- }
-
- if (package_mgr != nullptr) {
- std::vector<int> uids;
- std::vector<std::string> names;
- uids.push_back(uid);
- binder::Status status = package_mgr->getNamesForUids(uids, &names);
- if (!status.isOk()) {
- ALOGE("%s: getNamesForUids failed: %s",
- __func__, status.exceptionMessage().c_str());
- } else {
- if (!names[0].empty()) {
- pkg = names[0].c_str();
- }
- }
- }
-
- // strip any leading "shared:" strings that came back
- if (pkg.compare(0, 7, "shared:") == 0) {
- pkg.erase(0, 7);
- }
- // determine how pkg was installed and the versionCode
- if (pkg.empty()) {
- pkg = std::to_string(uid); // no name for us to manage
- } else if (strchr(pkg.c_str(), '.') == NULL) {
- // not of form 'com.whatever...'; assume internal and ok
- } else if (strncmp(pkg.c_str(), "android.", 8) == 0) {
- // android.* packages are assumed fine
- } else if (package_mgr.get() != nullptr) {
- String16 pkgName16(pkg.c_str());
- binder::Status status = package_mgr->getInstallerForPackage(pkgName16, &installer);
- if (!status.isOk()) {
- ALOGE("%s: getInstallerForPackage failed: %s",
- __func__, status.exceptionMessage().c_str());
- }
-
- // skip if we didn't get an installer
- if (status.isOk()) {
- status = package_mgr->getVersionCodeForPackage(pkgName16, &versionCode);
- if (!status.isOk()) {
- ALOGE("%s: getVersionCodeForPackage failed: %s",
- __func__, status.exceptionMessage().c_str());
- }
- }
-
- ALOGV("%s: package '%s' installed by '%s' versioncode %lld",
- __func__, pkg.c_str(), installer.c_str(), (long long)versionCode);
-
- if (strncmp(installer.c_str(), "com.android.", 12) == 0) {
- // from play store, we keep info
- } else if (strncmp(installer.c_str(), "com.google.", 11) == 0) {
- // some google source, we keep info
- } else if (strcmp(installer.c_str(), "preload") == 0) {
- // preloads, we keep the info
- } else if (installer.c_str()[0] == '\0') {
- // sideload (no installer); report UID only
- pkg = std::to_string(uid);
- versionCode = 0;
- } else {
- // unknown installer; report UID only
- pkg = std::to_string(uid);
- versionCode = 0;
- }
- } else {
- // unvalidated by package_mgr just send uid.
- pkg = std::to_string(uid);
- }
-
- // add it to the map, to save a subsequent lookup
- std::lock_guard _l(mUidInfoLock);
- // always overwrite
- mapping.uid = uid;
- mapping.pkg = std::move(pkg);
- mapping.installer = std::move(installer);
- mapping.versionCode = versionCode;
- mapping.expiration = now + PKG_EXPIRATION_NS;
- ALOGV("%s: adding uid %d pkg '%s' expiration: %lld",
- __func__, uid, mapping.pkg.c_str(), (long long)mapping.expiration);
- mPkgMappings[uid] = mapping;
- }
-
- if (mapping.uid != (uid_t)(-1)) {
- if (setName) {
- item->setPkgName(mapping.pkg);
- }
- if (setVersion) {
- item->setPkgVersionCode(mapping.versionCode);
- }
- }
-}
-
} // namespace android
diff --git a/services/mediametrics/MediaMetricsService.h b/services/mediametrics/MediaMetricsService.h
index b736c30..74a114a 100644
--- a/services/mediametrics/MediaMetricsService.h
+++ b/services/mediametrics/MediaMetricsService.h
@@ -24,6 +24,7 @@
// IMediaMetricsService must include Vector, String16, Errors
#include <media/IMediaMetricsService.h>
+#include <mediautils/ServiceUtilities.h>
#include <utils/String8.h>
#include "AudioAnalytics.h"
@@ -62,6 +63,11 @@
*/
static nsecs_t roundTime(nsecs_t timeNs);
+ /**
+ * Returns true if we should use uid for package name when uploading to WestWorld.
+ */
+ static bool useUidForPackage(const std::string& package, const std::string& installer);
+
protected:
// Internal call where release is true if ownership of item is transferred
@@ -96,27 +102,10 @@
const size_t mMaxRecordsExpiredAtOnce;
const int mDumpProtoDefault;
- class UidInfo {
- public:
- void setPkgInfo(mediametrics::Item *item, uid_t uid, bool setName, bool setVersion);
-
- private:
- std::mutex mUidInfoLock;
-
- struct UidToPkgInfo {
- uid_t uid = -1;
- std::string pkg;
- std::string installer;
- int64_t versionCode = 0;
- nsecs_t expiration = 0; // TODO: remove expiration.
- };
-
- // TODO: use concurrent hashmap with striped lock.
- std::unordered_map<uid_t, struct UidToPkgInfo> mPkgMappings; // GUARDED_BY(mUidInfoLock)
- } mUidInfo; // mUidInfo can be accessed without lock (locked internally)
-
std::atomic<int64_t> mItemsSubmitted{}; // accessed outside of lock.
+ mediautils::UidInfo mUidInfo; // mUidInfo can be accessed without lock (locked internally)
+
mediametrics::AudioAnalytics mAudioAnalytics;
std::mutex mLock;
diff --git a/services/mediametrics/TimeMachine.h b/services/mediametrics/TimeMachine.h
index b3932de..8682360 100644
--- a/services/mediametrics/TimeMachine.h
+++ b/services/mediametrics/TimeMachine.h
@@ -35,6 +35,14 @@
return s;
}
+// define a way of printing a std::pair.
+template <typename T, typename U>
+std::ostream & operator<< (std::ostream& s,
+ const std::pair<T, U>& v) {
+ s << "{ " << v.first << ", " << v.second << " }";
+ return s;
+}
+
// define a way of printing a variant
// see https://en.cppreference.com/w/cpp/utility/variant/visit
template <typename T0, typename ... Ts>
@@ -53,10 +61,12 @@
* The TimeMachine is NOT thread safe.
*/
class TimeMachine {
-
- using Elem = std::variant<std::monostate, int32_t, int64_t, double, std::string>;
+public:
+ using Elem = Item::Prop::Elem; // use the Item property element.
using PropertyHistory = std::multimap<int64_t /* time */, Elem>;
+private:
+
// KeyHistory contains no lock.
// Access is through the TimeMachine, and a hash-striped lock is used
// before calling into KeyHistory.
@@ -102,7 +112,8 @@
void putProp(
const std::string &name, const mediametrics::Item::Prop &prop, int64_t time = 0) {
- prop.visit([&](auto value) { putValue(name, value, time); });
+ //alternatively: prop.visit([&](auto value) { putValue(name, value, time); });
+ putValue(name, prop.get(), time);
}
template <typename T>
@@ -119,13 +130,6 @@
}
}
- // Explicitly ignore rate properties - we don't expose them for now.
- void putValue(
- const std::string &property __unused,
- std::pair<int64_t, int64_t>& e __unused,
- int64_t time __unused) {
- }
-
std::pair<std::string, int32_t> dump(int32_t lines, int64_t time) const {
std::stringstream ss;
int32_t ll = lines;
@@ -358,10 +362,10 @@
std::stringstream ss;
int32_t ll = lines;
- for (const auto &keyPair : mHistory) {
- std::lock_guard lock(getLockForKey(keyPair.first));
+ for (const auto &[lkey, lhist] : mHistory) {
+ std::lock_guard lock(getLockForKey(lkey));
if (lines <= 0) break;
- auto [s, l] = keyPair.second->dump(ll, time);
+ auto [s, l] = lhist->dump(ll, time);
ss << s;
ll -= l;
}
diff --git a/services/mediametrics/tests/Android.bp b/services/mediametrics/tests/Android.bp
index 9eb2d89..bdeda30 100644
--- a/services/mediametrics/tests/Android.bp
+++ b/services/mediametrics/tests/Android.bp
@@ -17,6 +17,7 @@
"liblog",
"libmediametrics",
"libmediametricsservice",
+ "libmediautils",
"libutils",
],
diff --git a/services/mediametrics/tests/mediametrics_tests.cpp b/services/mediametrics/tests/mediametrics_tests.cpp
index 55ce82b..90a8565 100644
--- a/services/mediametrics/tests/mediametrics_tests.cpp
+++ b/services/mediametrics/tests/mediametrics_tests.cpp
@@ -35,6 +35,23 @@
return count;
}
+TEST(mediametrics_tests, startsWith) {
+ std::string s("test");
+ ASSERT_EQ(true, android::mediametrics::startsWith(s, "te"));
+ ASSERT_EQ(true, android::mediametrics::startsWith(s, std::string("tes")));
+ ASSERT_EQ(false, android::mediametrics::startsWith(s, "ts"));
+ ASSERT_EQ(false, android::mediametrics::startsWith(s, std::string("est")));
+}
+
+TEST(mediametrics_tests, defer) {
+ bool check = false;
+ {
+ android::mediametrics::Defer defer([&] { check = true; });
+ ASSERT_EQ(false, check);
+ }
+ ASSERT_EQ(true, check);
+}
+
TEST(mediametrics_tests, instantiate) {
sp mediaMetrics = new MediaMetricsService();
status_t status;
@@ -72,6 +89,32 @@
mediaMetrics->dump(fileno(stdout), {} /* args */);
}
+TEST(mediametrics_tests, package_installer_check) {
+ ASSERT_EQ(false, MediaMetricsService::useUidForPackage(
+ "abcd", "installer")); // ok, package name has no dot.
+ ASSERT_EQ(false, MediaMetricsService::useUidForPackage(
+ "android.com", "installer")); // ok, package name starts with android
+
+ ASSERT_EQ(false, MediaMetricsService::useUidForPackage(
+ "abc.def", "com.android.foo")); // ok, installer name starts with com.android
+ ASSERT_EQ(false, MediaMetricsService::useUidForPackage(
+ "123.456", "com.google.bar")); // ok, installer name starts with com.google
+ ASSERT_EQ(false, MediaMetricsService::useUidForPackage(
+ "r2.d2", "preload")); // ok, installer name is preload
+
+ ASSERT_EQ(true, MediaMetricsService::useUidForPackage(
+ "abc.def", "installer")); // unknown installer
+ ASSERT_EQ(true, MediaMetricsService::useUidForPackage(
+ "123.456", "installer")); // unknown installer
+ ASSERT_EQ(true, MediaMetricsService::useUidForPackage(
+ "r2.d2", "preload23")); // unknown installer
+
+ ASSERT_EQ(true, MediaMetricsService::useUidForPackage(
+ "com.android.foo", "abc.def")); // unknown installer
+ ASSERT_EQ(true, MediaMetricsService::useUidForPackage(
+ "com.google.bar", "123.456")); // unknown installer
+}
+
TEST(mediametrics_tests, item_manipulation) {
mediametrics::Item item("audiorecord");
@@ -262,27 +305,32 @@
int32_t i32;
ASSERT_TRUE(prop.get(&i32));
ASSERT_EQ(1, i32);
+ ASSERT_EQ(1, std::get<int32_t>(prop.get()));
mask |= 1;
} else if (!strcmp(name, "i64")) {
int64_t i64;
ASSERT_TRUE(prop.get(&i64));
ASSERT_EQ(2, i64);
+ ASSERT_EQ(2, std::get<int64_t>(prop.get()));
mask |= 2;
} else if (!strcmp(name, "double")) {
double d;
ASSERT_TRUE(prop.get(&d));
ASSERT_EQ(3.125, d);
+ ASSERT_EQ(3.125, std::get<double>(prop.get()));
mask |= 4;
} else if (!strcmp(name, "string")) {
std::string s;
ASSERT_TRUE(prop.get(&s));
ASSERT_EQ("abc", s);
+ ASSERT_EQ(s, std::get<std::string>(prop.get()));
mask |= 8;
} else if (!strcmp(name, "rate")) {
std::pair<int64_t, int64_t> r;
ASSERT_TRUE(prop.get(&r));
ASSERT_EQ(11, r.first);
ASSERT_EQ(12, r.second);
+ ASSERT_EQ(r, std::get<decltype(r)>(prop.get()));
mask |= 16;
} else {
FAIL();