Merge "Fix android.media.cts.ImageReaderDecoderTest fail on MT6580 project"
diff --git a/drm/libmediadrm/Android.bp b/drm/libmediadrm/Android.bp
index 84f2f6d..52c7438 100644
--- a/drm/libmediadrm/Android.bp
+++ b/drm/libmediadrm/Android.bp
@@ -44,9 +44,11 @@
"libcutils",
"libdl",
"liblog",
+ "libmedia",
"libmediadrmmetrics_lite",
"libmediametrics",
"libmediautils",
+ "libresourcemanagerservice",
"libstagefright_foundation",
"libutils",
"android.hardware.drm@1.0",
diff --git a/drm/libmediadrm/DrmHal.cpp b/drm/libmediadrm/DrmHal.cpp
index e79fd4b..a2234e6 100644
--- a/drm/libmediadrm/DrmHal.cpp
+++ b/drm/libmediadrm/DrmHal.cpp
@@ -295,38 +295,45 @@
}
}
-
Mutex DrmHal::mLock;
-struct DrmSessionClient : public DrmSessionClientInterface {
- explicit DrmSessionClient(DrmHal* drm) : mDrm(drm) {}
-
- virtual bool reclaimSession(const Vector<uint8_t>& sessionId) {
- sp<DrmHal> drm = mDrm.promote();
- if (drm == NULL) {
- return true;
- }
- status_t err = drm->closeSession(sessionId);
- if (err != OK) {
- return false;
- }
- drm->sendEvent(EventType::SESSION_RECLAIMED,
- toHidlVec(sessionId), hidl_vec<uint8_t>());
+bool DrmHal::DrmSessionClient::reclaimResource() {
+ sp<DrmHal> drm = mDrm.promote();
+ if (drm == NULL) {
return true;
}
+ status_t err = drm->closeSession(mSessionId);
+ if (err != OK) {
+ return false;
+ }
+ drm->sendEvent(EventType::SESSION_RECLAIMED,
+ toHidlVec(mSessionId), hidl_vec<uint8_t>());
+ return true;
+}
-protected:
- virtual ~DrmSessionClient() {}
+String8 DrmHal::DrmSessionClient::getName() {
+ String8 name;
+ sp<DrmHal> drm = mDrm.promote();
+ if (drm == NULL) {
+ name.append("<deleted>");
+ } else if (drm->getPropertyStringInternal(String8("vendor"), name) != OK
+ || name.isEmpty()) {
+ name.append("<Get vendor failed or is empty>");
+ }
+ name.append("[");
+ for (size_t i = 0; i < mSessionId.size(); ++i) {
+ name.appendFormat("%02x", mSessionId[i]);
+ }
+ name.append("]");
+ return name;
+}
-private:
- wp<DrmHal> mDrm;
-
- DISALLOW_EVIL_CONSTRUCTORS(DrmSessionClient);
-};
+DrmHal::DrmSessionClient::~DrmSessionClient() {
+ DrmSessionManager::Instance()->removeSession(mSessionId);
+}
DrmHal::DrmHal()
- : mDrmSessionClient(new DrmSessionClient(this)),
- mFactories(makeDrmFactories()),
+ : mFactories(makeDrmFactories()),
mInitCheck((mFactories.size() == 0) ? ERROR_UNSUPPORTED : NO_INIT) {
}
@@ -335,14 +342,13 @@
auto openSessions = mOpenSessions;
for (size_t i = 0; i < openSessions.size(); i++) {
mLock.unlock();
- closeSession(openSessions[i]);
+ closeSession(openSessions[i]->mSessionId);
mLock.lock();
}
mOpenSessions.clear();
}
DrmHal::~DrmHal() {
- DrmSessionManager::Instance()->removeDrm(mDrmSessionClient);
}
void DrmHal::cleanup() {
@@ -748,9 +754,9 @@
} while (retry);
if (err == OK) {
- DrmSessionManager::Instance()->addSession(getCallingPid(),
- mDrmSessionClient, sessionId);
- mOpenSessions.push(sessionId);
+ sp<DrmSessionClient> client(new DrmSessionClient(this, sessionId));
+ DrmSessionManager::Instance()->addSession(getCallingPid(), client, sessionId);
+ mOpenSessions.push(client);
mMetrics.SetSessionStart(sessionId);
}
@@ -767,7 +773,7 @@
if (status == Status::OK) {
DrmSessionManager::Instance()->removeSession(sessionId);
for (size_t i = 0; i < mOpenSessions.size(); i++) {
- if (mOpenSessions[i] == sessionId) {
+ if (isEqualSessionId(mOpenSessions[i]->mSessionId, sessionId)) {
mOpenSessions.removeAt(i);
break;
}
diff --git a/drm/libmediadrm/DrmSessionManager.cpp b/drm/libmediadrm/DrmSessionManager.cpp
index 375644c..0b927ef 100644
--- a/drm/libmediadrm/DrmSessionManager.cpp
+++ b/drm/libmediadrm/DrmSessionManager.cpp
@@ -21,12 +21,17 @@
#include <binder/IPCThreadState.h>
#include <binder/IProcessInfoService.h>
#include <binder/IServiceManager.h>
-#include <media/stagefright/ProcessInfo.h>
-#include <mediadrm/DrmSessionClientInterface.h>
+#include <cutils/properties.h>
+#include <media/IResourceManagerClient.h>
+#include <media/MediaResource.h>
#include <mediadrm/DrmSessionManager.h>
#include <unistd.h>
#include <utils/String8.h>
+#include <vector>
+
+#include "ResourceManagerService.h"
+
namespace android {
static String8 GetSessionIdString(const Vector<uint8_t> &sessionId) {
@@ -37,6 +42,35 @@
return sessionIdStr;
}
+static std::vector<uint8_t> toStdVec(const Vector<uint8_t> &vector) {
+ const uint8_t *v = vector.array();
+ std::vector<uint8_t> vec(v, v + vector.size());
+ return vec;
+}
+
+static uint64_t toClientId(const sp<IResourceManagerClient>& drm) {
+ return reinterpret_cast<int64_t>(drm.get());
+}
+
+static Vector<MediaResource> toResourceVec(const Vector<uint8_t> &sessionId) {
+ Vector<MediaResource> resources;
+ // use UINT64_MAX to decrement through addition overflow
+ resources.push_back(MediaResource(MediaResource::kDrmSession, toStdVec(sessionId), UINT64_MAX));
+ return resources;
+}
+
+static sp<IResourceManagerService> getResourceManagerService() {
+ if (property_get_bool("persist.device_config.media_native.mediadrmserver", 1)) {
+ return new ResourceManagerService();
+ }
+ sp<IServiceManager> sm = defaultServiceManager();
+ if (sm == NULL) {
+ return NULL;
+ }
+ sp<IBinder> binder = sm->getService(String16("media.resource_manager"));
+ return interface_cast<IResourceManagerService>(binder);
+}
+
bool isEqualSessionId(const Vector<uint8_t> &sessionId1, const Vector<uint8_t> &sessionId2) {
if (sessionId1.size() != sessionId2.size()) {
return false;
@@ -51,189 +85,114 @@
sp<DrmSessionManager> DrmSessionManager::Instance() {
static sp<DrmSessionManager> drmSessionManager = new DrmSessionManager();
+ drmSessionManager->init();
return drmSessionManager;
}
DrmSessionManager::DrmSessionManager()
- : mProcessInfo(new ProcessInfo()),
- mTime(0) {}
+ : DrmSessionManager(getResourceManagerService()) {
+}
-DrmSessionManager::DrmSessionManager(sp<ProcessInfoInterface> processInfo)
- : mProcessInfo(processInfo),
- mTime(0) {}
+DrmSessionManager::DrmSessionManager(const sp<IResourceManagerService> &service)
+ : mService(service),
+ mInitialized(false) {
+ if (mService == NULL) {
+ ALOGE("Failed to init ResourceManagerService");
+ }
+}
-DrmSessionManager::~DrmSessionManager() {}
+DrmSessionManager::~DrmSessionManager() {
+ if (mService != NULL) {
+ IInterface::asBinder(mService)->unlinkToDeath(this);
+ }
+}
-void DrmSessionManager::addSession(
- int pid, const sp<DrmSessionClientInterface>& drm, const Vector<uint8_t> &sessionId) {
- ALOGV("addSession(pid %d, drm %p, sessionId %s)", pid, drm.get(),
+void DrmSessionManager::init() {
+ Mutex::Autolock lock(mLock);
+ if (mInitialized) {
+ return;
+ }
+ mInitialized = true;
+ if (mService != NULL) {
+ IInterface::asBinder(mService)->linkToDeath(this);
+ }
+}
+
+void DrmSessionManager::addSession(int pid,
+ const sp<IResourceManagerClient>& drm, const Vector<uint8_t> &sessionId) {
+ uid_t uid = IPCThreadState::self()->getCallingUid();
+ ALOGV("addSession(pid %d, uid %d, drm %p, sessionId %s)", pid, uid, drm.get(),
GetSessionIdString(sessionId).string());
Mutex::Autolock lock(mLock);
- SessionInfo info;
- info.drm = drm;
- info.sessionId = sessionId;
- info.timeStamp = getTime_l();
- ssize_t index = mSessionMap.indexOfKey(pid);
- if (index < 0) {
- // new pid
- SessionInfos infosForPid;
- infosForPid.push_back(info);
- mSessionMap.add(pid, infosForPid);
- } else {
- mSessionMap.editValueAt(index).push_back(info);
+ if (mService == NULL) {
+ return;
}
+
+ int64_t clientId = toClientId(drm);
+ mSessionMap[toStdVec(sessionId)] = (SessionInfo){pid, uid, clientId};
+ mService->addResource(pid, uid, clientId, drm, toResourceVec(sessionId));
}
void DrmSessionManager::useSession(const Vector<uint8_t> &sessionId) {
ALOGV("useSession(%s)", GetSessionIdString(sessionId).string());
Mutex::Autolock lock(mLock);
- for (size_t i = 0; i < mSessionMap.size(); ++i) {
- SessionInfos& infos = mSessionMap.editValueAt(i);
- for (size_t j = 0; j < infos.size(); ++j) {
- SessionInfo& info = infos.editItemAt(j);
- if (isEqualSessionId(sessionId, info.sessionId)) {
- info.timeStamp = getTime_l();
- return;
- }
- }
+ auto it = mSessionMap.find(toStdVec(sessionId));
+ if (mService == NULL || it == mSessionMap.end()) {
+ return;
}
+
+ auto info = it->second;
+ mService->addResource(info.pid, info.uid, info.clientId, NULL, toResourceVec(sessionId));
}
void DrmSessionManager::removeSession(const Vector<uint8_t> &sessionId) {
ALOGV("removeSession(%s)", GetSessionIdString(sessionId).string());
Mutex::Autolock lock(mLock);
- for (size_t i = 0; i < mSessionMap.size(); ++i) {
- SessionInfos& infos = mSessionMap.editValueAt(i);
- for (size_t j = 0; j < infos.size(); ++j) {
- if (isEqualSessionId(sessionId, infos[j].sessionId)) {
- infos.removeAt(j);
- return;
- }
- }
+ auto it = mSessionMap.find(toStdVec(sessionId));
+ if (mService == NULL || it == mSessionMap.end()) {
+ return;
}
-}
-void DrmSessionManager::removeDrm(const sp<DrmSessionClientInterface>& drm) {
- ALOGV("removeDrm(%p)", drm.get());
-
- Mutex::Autolock lock(mLock);
- bool found = false;
- for (size_t i = 0; i < mSessionMap.size(); ++i) {
- SessionInfos& infos = mSessionMap.editValueAt(i);
- for (size_t j = 0; j < infos.size();) {
- if (infos[j].drm == drm) {
- ALOGV("removed session (%s)", GetSessionIdString(infos[j].sessionId).string());
- j = infos.removeAt(j);
- found = true;
- } else {
- ++j;
- }
- }
- if (found) {
- break;
- }
- }
+ auto info = it->second;
+ mService->removeResource(info.pid, info.clientId, toResourceVec(sessionId));
+ mSessionMap.erase(it);
}
bool DrmSessionManager::reclaimSession(int callingPid) {
ALOGV("reclaimSession(%d)", callingPid);
- sp<DrmSessionClientInterface> drm;
- Vector<uint8_t> sessionId;
- int lowestPriorityPid;
- int lowestPriority;
- {
- Mutex::Autolock lock(mLock);
- int callingPriority;
- if (!mProcessInfo->getPriority(callingPid, &callingPriority)) {
- return false;
- }
- if (!getLowestPriority_l(&lowestPriorityPid, &lowestPriority)) {
- return false;
- }
- if (lowestPriority <= callingPriority) {
- return false;
- }
+ // unlock early because reclaimResource might callback into removeSession
+ mLock.lock();
+ sp<IResourceManagerService> service(mService);
+ mLock.unlock();
- if (!getLeastUsedSession_l(lowestPriorityPid, &drm, &sessionId)) {
- return false;
- }
- }
-
- if (drm == NULL) {
+ if (service == NULL) {
return false;
}
- ALOGV("reclaim session(%s) opened by pid %d",
- GetSessionIdString(sessionId).string(), lowestPriorityPid);
-
- return drm->reclaimSession(sessionId);
+ // cannot update mSessionMap because we do not know which sessionId is reclaimed;
+ // we rely on IResourceManagerClient to removeSession in reclaimResource
+ Vector<uint8_t> dummy;
+ return service->reclaimResource(callingPid, toResourceVec(dummy));
}
-int64_t DrmSessionManager::getTime_l() {
- return mTime++;
+size_t DrmSessionManager::getSessionCount() const {
+ Mutex::Autolock lock(mLock);
+ return mSessionMap.size();
}
-bool DrmSessionManager::getLowestPriority_l(int* lowestPriorityPid, int* lowestPriority) {
- int pid = -1;
- int priority = -1;
- for (size_t i = 0; i < mSessionMap.size(); ++i) {
- if (mSessionMap.valueAt(i).size() == 0) {
- // no opened session by this process.
- continue;
- }
- int tempPid = mSessionMap.keyAt(i);
- int tempPriority;
- if (!mProcessInfo->getPriority(tempPid, &tempPriority)) {
- // shouldn't happen.
- return false;
- }
- if (pid == -1) {
- pid = tempPid;
- priority = tempPriority;
- } else {
- if (tempPriority > priority) {
- pid = tempPid;
- priority = tempPriority;
- }
- }
- }
- if (pid != -1) {
- *lowestPriorityPid = pid;
- *lowestPriority = priority;
- }
- return (pid != -1);
+bool DrmSessionManager::containsSession(const Vector<uint8_t>& sessionId) const {
+ Mutex::Autolock lock(mLock);
+ return mSessionMap.count(toStdVec(sessionId));
}
-bool DrmSessionManager::getLeastUsedSession_l(
- int pid, sp<DrmSessionClientInterface>* drm, Vector<uint8_t>* sessionId) {
- ssize_t index = mSessionMap.indexOfKey(pid);
- if (index < 0) {
- return false;
- }
-
- int leastUsedIndex = -1;
- int64_t minTs = LLONG_MAX;
- const SessionInfos& infos = mSessionMap.valueAt(index);
- for (size_t j = 0; j < infos.size(); ++j) {
- if (leastUsedIndex == -1) {
- leastUsedIndex = j;
- minTs = infos[j].timeStamp;
- } else {
- if (infos[j].timeStamp < minTs) {
- leastUsedIndex = j;
- minTs = infos[j].timeStamp;
- }
- }
- }
- if (leastUsedIndex != -1) {
- *drm = infos[leastUsedIndex].drm;
- *sessionId = infos[leastUsedIndex].sessionId;
- }
- return (leastUsedIndex != -1);
+void DrmSessionManager::binderDied(const wp<IBinder>& /*who*/) {
+ ALOGW("ResourceManagerService died.");
+ Mutex::Autolock lock(mLock);
+ mService.clear();
}
} // namespace android
diff --git a/drm/libmediadrm/include/mediadrm/DrmHal.h b/drm/libmediadrm/include/mediadrm/DrmHal.h
index bdf1b30..542d300 100644
--- a/drm/libmediadrm/include/mediadrm/DrmHal.h
+++ b/drm/libmediadrm/include/mediadrm/DrmHal.h
@@ -26,8 +26,10 @@
#include <android/hardware/drm/1.2/IDrmPlugin.h>
#include <android/hardware/drm/1.2/IDrmPluginListener.h>
+#include <media/IResourceManagerService.h>
#include <media/MediaAnalyticsItem.h>
#include <mediadrm/DrmMetrics.h>
+#include <mediadrm/DrmSessionManager.h>
#include <mediadrm/IDrm.h>
#include <mediadrm/IDrmClient.h>
#include <utils/threads.h>
@@ -59,6 +61,26 @@
struct DrmHal : public BnDrm,
public IBinder::DeathRecipient,
public IDrmPluginListener_V1_2 {
+
+ struct DrmSessionClient : public BnResourceManagerClient {
+ explicit DrmSessionClient(DrmHal* drm, const Vector<uint8_t>& sessionId)
+ : mSessionId(sessionId),
+ mDrm(drm) {}
+
+ virtual bool reclaimResource();
+ virtual String8 getName();
+
+ const Vector<uint8_t> mSessionId;
+
+ protected:
+ virtual ~DrmSessionClient();
+
+ private:
+ wp<DrmHal> mDrm;
+
+ DISALLOW_EVIL_CONSTRUCTORS(DrmSessionClient);
+ };
+
DrmHal();
virtual ~DrmHal();
@@ -193,8 +215,6 @@
private:
static Mutex mLock;
- sp<DrmSessionClientInterface> mDrmSessionClient;
-
sp<IDrmClient> mListener;
mutable Mutex mEventLock;
mutable Mutex mNotifyLock;
@@ -208,7 +228,7 @@
// Mutable to allow modification within GetPropertyByteArray.
mutable MediaDrmMetrics mMetrics;
- Vector<Vector<uint8_t>> mOpenSessions;
+ Vector<sp<DrmSessionClient>> mOpenSessions;
void closeOpenSessions();
void cleanup();
diff --git a/drm/libmediadrm/include/mediadrm/DrmSessionManager.h b/drm/libmediadrm/include/mediadrm/DrmSessionManager.h
index ba27199..b1ad580 100644
--- a/drm/libmediadrm/include/mediadrm/DrmSessionManager.h
+++ b/drm/libmediadrm/include/mediadrm/DrmSessionManager.h
@@ -18,56 +18,61 @@
#define DRM_SESSION_MANAGER_H_
+#include <binder/IBinder.h>
+#include <media/IResourceManagerService.h>
#include <media/stagefright/foundation/ABase.h>
#include <utils/RefBase.h>
#include <utils/KeyedVector.h>
#include <utils/threads.h>
#include <utils/Vector.h>
+#include <map>
+#include <utility>
+#include <vector>
+
namespace android {
class DrmSessionManagerTest;
-struct DrmSessionClientInterface;
-struct ProcessInfoInterface;
+class IResourceManagerClient;
bool isEqualSessionId(const Vector<uint8_t> &sessionId1, const Vector<uint8_t> &sessionId2);
struct SessionInfo {
- sp<DrmSessionClientInterface> drm;
- Vector<uint8_t> sessionId;
- int64_t timeStamp;
+ pid_t pid;
+ uid_t uid;
+ int64_t clientId;
};
-typedef Vector<SessionInfo > SessionInfos;
-typedef KeyedVector<int, SessionInfos > PidSessionInfosMap;
+typedef std::map<std::vector<uint8_t>, SessionInfo> SessionInfoMap;
-struct DrmSessionManager : public RefBase {
+struct DrmSessionManager : public IBinder::DeathRecipient {
static sp<DrmSessionManager> Instance();
DrmSessionManager();
- explicit DrmSessionManager(sp<ProcessInfoInterface> processInfo);
+ explicit DrmSessionManager(const sp<IResourceManagerService> &service);
- void addSession(int pid, const sp<DrmSessionClientInterface>& drm, const Vector<uint8_t>& sessionId);
+ void addSession(int pid, const sp<IResourceManagerClient>& drm, const Vector<uint8_t>& sessionId);
void useSession(const Vector<uint8_t>& sessionId);
void removeSession(const Vector<uint8_t>& sessionId);
- void removeDrm(const sp<DrmSessionClientInterface>& drm);
bool reclaimSession(int callingPid);
+ // sanity check APIs
+ size_t getSessionCount() const;
+ bool containsSession(const Vector<uint8_t>& sessionId) const;
+
+ // implements DeathRecipient
+ virtual void binderDied(const wp<IBinder>& /*who*/);
+
protected:
virtual ~DrmSessionManager();
private:
- friend class DrmSessionManagerTest;
+ void init();
- int64_t getTime_l();
- bool getLowestPriority_l(int* lowestPriorityPid, int* lowestPriority);
- bool getLeastUsedSession_l(
- int pid, sp<DrmSessionClientInterface>* drm, Vector<uint8_t>* sessionId);
-
- sp<ProcessInfoInterface> mProcessInfo;
+ sp<IResourceManagerService> mService;
mutable Mutex mLock;
- PidSessionInfosMap mSessionMap;
- int64_t mTime;
+ SessionInfoMap mSessionMap;
+ bool mInitialized;
DISALLOW_EVIL_CONSTRUCTORS(DrmSessionManager);
};
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp
index 2efb987..375ce17 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.cpp
+++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp
@@ -29,6 +29,7 @@
#include <android/hardware/cas/native/1.0/IDescrambler.h>
#include <android-base/stringprintf.h>
#include <binder/MemoryDealer.h>
+#include <cutils/properties.h>
#include <gui/Surface.h>
#include <media/openmax/OMX_Core.h>
#include <media/stagefright/foundation/ABuffer.h>
diff --git a/media/codec2/vndk/Android.bp b/media/codec2/vndk/Android.bp
index 52cc7ad..6fbad0a 100644
--- a/media/codec2/vndk/Android.bp
+++ b/media/codec2/vndk/Android.bp
@@ -71,6 +71,8 @@
"libutils",
],
+ tidy: false, // b/146435095, clang-tidy segmentation fault
+
cflags: [
"-Werror",
"-Wall",
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 1744d3d..650d79d 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 1d9e1e6..14bd644 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/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp
index cb9c7ba..480930b 100644
--- a/media/libaudioclient/AudioSystem.cpp
+++ b/media/libaudioclient/AudioSystem.cpp
@@ -1491,7 +1491,14 @@
}
}
}
- ALOGE("invalid attributes %s when converting to stream", toString(attr).c_str());
+ switch (attr.usage) {
+ case AUDIO_USAGE_VIRTUAL_SOURCE:
+ // virtual source is not expected to have an associated product strategy
+ break;
+ default:
+ ALOGE("invalid attributes %s when converting to stream", toString(attr).c_str());
+ break;
+ }
return AUDIO_STREAM_MUSIC;
}
diff --git a/media/libmedia/MediaResource.cpp b/media/libmedia/MediaResource.cpp
index e636a50..8626009 100644
--- a/media/libmedia/MediaResource.cpp
+++ b/media/libmedia/MediaResource.cpp
@@ -19,6 +19,8 @@
#include <utils/Log.h>
#include <media/MediaResource.h>
+#include <vector>
+
namespace android {
MediaResource::MediaResource()
@@ -36,26 +38,48 @@
mSubType(subType),
mValue(value) {}
+MediaResource::MediaResource(Type type, const std::vector<uint8_t> &id, uint64_t value)
+ : mType(type),
+ mSubType(kUnspecifiedSubType),
+ mValue(value),
+ mId(id) {}
+
void MediaResource::readFromParcel(const Parcel &parcel) {
mType = static_cast<Type>(parcel.readInt32());
mSubType = static_cast<SubType>(parcel.readInt32());
mValue = parcel.readUint64();
+ parcel.readByteVector(&mId);
}
void MediaResource::writeToParcel(Parcel *parcel) const {
parcel->writeInt32(static_cast<int32_t>(mType));
parcel->writeInt32(static_cast<int32_t>(mSubType));
parcel->writeUint64(mValue);
+ parcel->writeByteVector(mId);
+}
+
+static String8 bytesToHexString(const std::vector<uint8_t> &bytes) {
+ String8 str;
+ for (auto &b : bytes) {
+ str.appendFormat("%02x", b);
+ }
+ return str;
}
String8 MediaResource::toString() const {
String8 str;
- str.appendFormat("%s/%s:%llu", asString(mType), asString(mSubType), (unsigned long long)mValue);
+ str.appendFormat("%s/%s:[%s]:%llu",
+ asString(mType), asString(mSubType),
+ bytesToHexString(mId).c_str(),
+ (unsigned long long)mValue);
return str;
}
bool MediaResource::operator==(const MediaResource &other) const {
- return (other.mType == mType) && (other.mSubType == mSubType) && (other.mValue == mValue);
+ return (other.mType == mType)
+ && (other.mSubType == mSubType)
+ && (other.mValue == mValue)
+ && (other.mId == mId);
}
bool MediaResource::operator!=(const MediaResource &other) const {
diff --git a/media/libmedia/include/media/MediaResource.h b/media/libmedia/include/media/MediaResource.h
index 10a07bb..e9684f0 100644
--- a/media/libmedia/include/media/MediaResource.h
+++ b/media/libmedia/include/media/MediaResource.h
@@ -20,6 +20,7 @@
#include <binder/Parcel.h>
#include <utils/String8.h>
+#include <vector>
namespace android {
@@ -32,6 +33,7 @@
kGraphicMemory,
kCpuBoost,
kBattery,
+ kDrmSession,
};
enum SubType {
@@ -43,6 +45,7 @@
MediaResource();
MediaResource(Type type, uint64_t value);
MediaResource(Type type, SubType subType, uint64_t value);
+ MediaResource(Type type, const std::vector<uint8_t> &id, uint64_t value);
void readFromParcel(const Parcel &parcel);
void writeToParcel(Parcel *parcel) const;
@@ -55,6 +58,8 @@
Type mType;
SubType mSubType;
uint64_t mValue;
+ // for kDrmSession-type mId is the unique session id obtained via MediaDrm#openSession
+ std::vector<uint8_t> mId;
};
inline static const char *asString(MediaResource::Type i, const char *def = "??") {
@@ -65,6 +70,7 @@
case MediaResource::kGraphicMemory: return "graphic-memory";
case MediaResource::kCpuBoost: return "cpu-boost";
case MediaResource::kBattery: return "battery";
+ case MediaResource::kDrmSession: return "drm-session";
default: return def;
}
}
diff --git a/media/libmediametrics/MediaAnalyticsItem.cpp b/media/libmediametrics/MediaAnalyticsItem.cpp
index 02c23b1..b7856a6 100644
--- a/media/libmediametrics/MediaAnalyticsItem.cpp
+++ b/media/libmediametrics/MediaAnalyticsItem.cpp
@@ -64,6 +64,16 @@
return item;
}
+MediaAnalyticsItem* MediaAnalyticsItem::convert(mediametrics_handle_t handle) {
+ MediaAnalyticsItem *item = (android::MediaAnalyticsItem *) handle;
+ return item;
+}
+
+mediametrics_handle_t MediaAnalyticsItem::convert(MediaAnalyticsItem *item ) {
+ mediametrics_handle_t handle = (mediametrics_handle_t) item;
+ return handle;
+}
+
// access functions for the class
MediaAnalyticsItem::MediaAnalyticsItem()
: mPid(-1),
diff --git a/media/libmediametrics/MediaMetrics.cpp b/media/libmediametrics/MediaMetrics.cpp
index 6109190..360ae0c 100644
--- a/media/libmediametrics/MediaMetrics.cpp
+++ b/media/libmediametrics/MediaMetrics.cpp
@@ -169,6 +169,11 @@
return item->selfrecord();
}
+mediametrics_handle_t mediametrics_dup(mediametrics_handle_t handle) {
+ android::MediaAnalyticsItem *item = (android::MediaAnalyticsItem *) handle;
+ if (item == NULL) return android::MediaAnalyticsItem::convert(item);
+ return android::MediaAnalyticsItem::convert(item->dup());
+}
const char *mediametrics_readable(mediametrics_handle_t handle) {
android::MediaAnalyticsItem *item = (android::MediaAnalyticsItem *) handle;
diff --git a/media/libmediametrics/include/MediaAnalyticsItem.h b/media/libmediametrics/include/MediaAnalyticsItem.h
index 4a36f6a..42a2f5b 100644
--- a/media/libmediametrics/include/MediaAnalyticsItem.h
+++ b/media/libmediametrics/include/MediaAnalyticsItem.h
@@ -17,6 +17,8 @@
#ifndef ANDROID_MEDIA_MEDIAANALYTICSITEM_H
#define ANDROID_MEDIA_MEDIAANALYTICSITEM_H
+#include "MediaMetrics.h"
+
#include <string>
#include <sys/types.h>
@@ -94,6 +96,9 @@
static MediaAnalyticsItem* create(Key key);
static MediaAnalyticsItem* create();
+ static MediaAnalyticsItem* convert(mediametrics_handle_t);
+ static mediametrics_handle_t convert(MediaAnalyticsItem *);
+
// access functions for the class
~MediaAnalyticsItem();
diff --git a/media/libmediametrics/include/MediaMetrics.h b/media/libmediametrics/include/MediaMetrics.h
index a4e1ed2..29fb241 100644
--- a/media/libmediametrics/include/MediaMetrics.h
+++ b/media/libmediametrics/include/MediaMetrics.h
@@ -79,6 +79,7 @@
// # of attributes set within this record.
int32_t mediametrics_count(mediametrics_handle_t handle);
+mediametrics_handle_t mediametrics_dup(mediametrics_handle_t handle);
bool mediametrics_selfRecord(mediametrics_handle_t handle);
const char *mediametrics_readable(mediametrics_handle_t handle);
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index 3388097..c1c4b55 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -1798,7 +1798,9 @@
}
void NuPlayer::closeAudioSink() {
- mRenderer->closeAudioSink();
+ if (mRenderer != NULL) {
+ mRenderer->closeAudioSink();
+ }
}
void NuPlayer::restartAudio(
diff --git a/media/libmediaplayerservice/tests/Android.bp b/media/libmediaplayerservice/tests/Android.bp
index f8c89e5..8357925 100644
--- a/media/libmediaplayerservice/tests/Android.bp
+++ b/media/libmediaplayerservice/tests/Android.bp
@@ -6,14 +6,22 @@
shared_libs: [
"liblog",
+ "libbinder",
+ "libmedia",
"libmediaplayerservice",
"libmediadrm",
+ "libresourcemanagerservice",
"libutils",
"android.hardware.drm@1.0",
"android.hardware.drm@1.1",
"android.hardware.drm@1.2",
],
+ include_dirs: [
+ "frameworks/av/include",
+ "frameworks/av/services/mediaresourcemanager",
+ ],
+
cflags: [
"-Werror",
"-Wall",
diff --git a/media/libmediaplayerservice/tests/DrmSessionManager_test.cpp b/media/libmediaplayerservice/tests/DrmSessionManager_test.cpp
index d81ee05..58e4bee 100644
--- a/media/libmediaplayerservice/tests/DrmSessionManager_test.cpp
+++ b/media/libmediaplayerservice/tests/DrmSessionManager_test.cpp
@@ -20,14 +20,29 @@
#include <gtest/gtest.h>
+#include <media/IResourceManagerService.h>
+#include <media/IResourceManagerClient.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/ProcessInfoInterface.h>
#include <mediadrm/DrmHal.h>
#include <mediadrm/DrmSessionClientInterface.h>
#include <mediadrm/DrmSessionManager.h>
+#include <algorithm>
+#include <vector>
+
+#include "ResourceManagerService.h"
+
namespace android {
+static Vector<uint8_t> toAndroidVector(const std::vector<uint8_t> &vec) {
+ Vector<uint8_t> aVec;
+ for (auto b : vec) {
+ aVec.push_back(b);
+ }
+ return aVec;
+}
+
struct FakeProcessInfo : public ProcessInfoInterface {
FakeProcessInfo() {}
virtual ~FakeProcessInfo() {}
@@ -47,173 +62,128 @@
DISALLOW_EVIL_CONSTRUCTORS(FakeProcessInfo);
};
-struct FakeDrm : public DrmSessionClientInterface {
- FakeDrm() {}
+struct FakeDrm : public BnResourceManagerClient {
+ FakeDrm(const std::vector<uint8_t>& sessionId, const sp<DrmSessionManager>& manager)
+ : mSessionId(toAndroidVector(sessionId)),
+ mReclaimed(false),
+ mDrmSessionManager(manager) {}
+
virtual ~FakeDrm() {}
- virtual bool reclaimSession(const Vector<uint8_t>& sessionId) {
- mReclaimedSessions.push_back(sessionId);
+ virtual bool reclaimResource() {
+ mReclaimed = true;
+ mDrmSessionManager->removeSession(mSessionId);
return true;
}
- const Vector<Vector<uint8_t> >& reclaimedSessions() const {
- return mReclaimedSessions;
+ virtual String8 getName() {
+ String8 name("FakeDrm[");
+ for (size_t i = 0; i < mSessionId.size(); ++i) {
+ name.appendFormat("%02x", mSessionId[i]);
+ }
+ name.append("]");
+ return name;
}
+ bool isReclaimed() const {
+ return mReclaimed;
+ }
+
+ const Vector<uint8_t> mSessionId;
+
private:
- Vector<Vector<uint8_t> > mReclaimedSessions;
+ bool mReclaimed;
+ const sp<DrmSessionManager> mDrmSessionManager;
DISALLOW_EVIL_CONSTRUCTORS(FakeDrm);
};
+struct FakeSystemCallback :
+ public ResourceManagerService::SystemCallbackInterface {
+ FakeSystemCallback() {}
+
+ virtual void noteStartVideo(int /*uid*/) override {}
+
+ virtual void noteStopVideo(int /*uid*/) override {}
+
+ virtual void noteResetVideo() override {}
+
+ virtual bool requestCpusetBoost(
+ bool /*enable*/, const sp<IInterface> &/*client*/) override {
+ return true;
+ }
+
+protected:
+ virtual ~FakeSystemCallback() {}
+
+private:
+
+ DISALLOW_EVIL_CONSTRUCTORS(FakeSystemCallback);
+};
+
static const int kTestPid1 = 30;
static const int kTestPid2 = 20;
-static const uint8_t kTestSessionId1[] = {1, 2, 3};
-static const uint8_t kTestSessionId2[] = {4, 5, 6, 7, 8};
-static const uint8_t kTestSessionId3[] = {9, 0};
+static const std::vector<uint8_t> kTestSessionId1{1, 2, 3};
+static const std::vector<uint8_t> kTestSessionId2{4, 5, 6, 7, 8};
+static const std::vector<uint8_t> kTestSessionId3{9, 0};
class DrmSessionManagerTest : public ::testing::Test {
public:
DrmSessionManagerTest()
- : mDrmSessionManager(new DrmSessionManager(new FakeProcessInfo())),
- mTestDrm1(new FakeDrm()),
- mTestDrm2(new FakeDrm()) {
- GetSessionId(kTestSessionId1, ARRAY_SIZE(kTestSessionId1), &mSessionId1);
- GetSessionId(kTestSessionId2, ARRAY_SIZE(kTestSessionId2), &mSessionId2);
- GetSessionId(kTestSessionId3, ARRAY_SIZE(kTestSessionId3), &mSessionId3);
+ : mService(new ResourceManagerService(new FakeProcessInfo(), new FakeSystemCallback())),
+ mDrmSessionManager(new DrmSessionManager(mService)),
+ mTestDrm1(new FakeDrm(kTestSessionId1, mDrmSessionManager)),
+ mTestDrm2(new FakeDrm(kTestSessionId2, mDrmSessionManager)),
+ mTestDrm3(new FakeDrm(kTestSessionId3, mDrmSessionManager)) {
+ DrmSessionManager *ptr = new DrmSessionManager(mService);
+ EXPECT_NE(ptr, nullptr);
+ /* mDrmSessionManager = ptr; */
}
protected:
- static void GetSessionId(const uint8_t* ids, size_t num, Vector<uint8_t>* sessionId) {
- for (size_t i = 0; i < num; ++i) {
- sessionId->push_back(ids[i]);
- }
- }
-
- static void ExpectEqSessionInfo(const SessionInfo& info, sp<DrmSessionClientInterface> drm,
- const Vector<uint8_t>& sessionId, int64_t timeStamp) {
- EXPECT_EQ(drm, info.drm);
- EXPECT_TRUE(isEqualSessionId(sessionId, info.sessionId));
- EXPECT_EQ(timeStamp, info.timeStamp);
- }
-
void addSession() {
- mDrmSessionManager->addSession(kTestPid1, mTestDrm1, mSessionId1);
- mDrmSessionManager->addSession(kTestPid2, mTestDrm2, mSessionId2);
- mDrmSessionManager->addSession(kTestPid2, mTestDrm2, mSessionId3);
- const PidSessionInfosMap& map = sessionMap();
- EXPECT_EQ(2u, map.size());
- ssize_t index1 = map.indexOfKey(kTestPid1);
- ASSERT_GE(index1, 0);
- const SessionInfos& infos1 = map[index1];
- EXPECT_EQ(1u, infos1.size());
- ExpectEqSessionInfo(infos1[0], mTestDrm1, mSessionId1, 0);
-
- ssize_t index2 = map.indexOfKey(kTestPid2);
- ASSERT_GE(index2, 0);
- const SessionInfos& infos2 = map[index2];
- EXPECT_EQ(2u, infos2.size());
- ExpectEqSessionInfo(infos2[0], mTestDrm2, mSessionId2, 1);
- ExpectEqSessionInfo(infos2[1], mTestDrm2, mSessionId3, 2);
+ mDrmSessionManager->addSession(kTestPid1, mTestDrm1, mTestDrm1->mSessionId);
+ mDrmSessionManager->addSession(kTestPid2, mTestDrm2, mTestDrm2->mSessionId);
+ mDrmSessionManager->addSession(kTestPid2, mTestDrm3, mTestDrm3->mSessionId);
}
- const PidSessionInfosMap& sessionMap() {
- return mDrmSessionManager->mSessionMap;
- }
-
- void testGetLowestPriority() {
- int pid;
- int priority;
- EXPECT_FALSE(mDrmSessionManager->getLowestPriority_l(&pid, &priority));
-
- addSession();
- EXPECT_TRUE(mDrmSessionManager->getLowestPriority_l(&pid, &priority));
-
- EXPECT_EQ(kTestPid1, pid);
- FakeProcessInfo processInfo;
- int priority1;
- processInfo.getPriority(kTestPid1, &priority1);
- EXPECT_EQ(priority1, priority);
- }
-
- void testGetLeastUsedSession() {
- sp<DrmSessionClientInterface> drm;
- Vector<uint8_t> sessionId;
- EXPECT_FALSE(mDrmSessionManager->getLeastUsedSession_l(kTestPid1, &drm, &sessionId));
-
- addSession();
-
- EXPECT_TRUE(mDrmSessionManager->getLeastUsedSession_l(kTestPid1, &drm, &sessionId));
- EXPECT_EQ(mTestDrm1, drm);
- EXPECT_TRUE(isEqualSessionId(mSessionId1, sessionId));
-
- EXPECT_TRUE(mDrmSessionManager->getLeastUsedSession_l(kTestPid2, &drm, &sessionId));
- EXPECT_EQ(mTestDrm2, drm);
- EXPECT_TRUE(isEqualSessionId(mSessionId2, sessionId));
-
- // mSessionId2 is no longer the least used session.
- mDrmSessionManager->useSession(mSessionId2);
- EXPECT_TRUE(mDrmSessionManager->getLeastUsedSession_l(kTestPid2, &drm, &sessionId));
- EXPECT_EQ(mTestDrm2, drm);
- EXPECT_TRUE(isEqualSessionId(mSessionId3, sessionId));
- }
-
+ sp<IResourceManagerService> mService;
sp<DrmSessionManager> mDrmSessionManager;
sp<FakeDrm> mTestDrm1;
sp<FakeDrm> mTestDrm2;
- Vector<uint8_t> mSessionId1;
- Vector<uint8_t> mSessionId2;
- Vector<uint8_t> mSessionId3;
+ sp<FakeDrm> mTestDrm3;
};
TEST_F(DrmSessionManagerTest, addSession) {
addSession();
+
+ EXPECT_EQ(3u, mDrmSessionManager->getSessionCount());
+ EXPECT_TRUE(mDrmSessionManager->containsSession(mTestDrm1->mSessionId));
+ EXPECT_TRUE(mDrmSessionManager->containsSession(mTestDrm2->mSessionId));
+ EXPECT_TRUE(mDrmSessionManager->containsSession(mTestDrm3->mSessionId));
}
TEST_F(DrmSessionManagerTest, useSession) {
addSession();
- mDrmSessionManager->useSession(mSessionId1);
- mDrmSessionManager->useSession(mSessionId3);
+ mDrmSessionManager->useSession(mTestDrm1->mSessionId);
+ mDrmSessionManager->useSession(mTestDrm3->mSessionId);
- const PidSessionInfosMap& map = sessionMap();
- const SessionInfos& infos1 = map.valueFor(kTestPid1);
- const SessionInfos& infos2 = map.valueFor(kTestPid2);
- ExpectEqSessionInfo(infos1[0], mTestDrm1, mSessionId1, 3);
- ExpectEqSessionInfo(infos2[1], mTestDrm2, mSessionId3, 4);
+ EXPECT_EQ(3u, mDrmSessionManager->getSessionCount());
+ EXPECT_TRUE(mDrmSessionManager->containsSession(mTestDrm1->mSessionId));
+ EXPECT_TRUE(mDrmSessionManager->containsSession(mTestDrm2->mSessionId));
+ EXPECT_TRUE(mDrmSessionManager->containsSession(mTestDrm3->mSessionId));
}
TEST_F(DrmSessionManagerTest, removeSession) {
addSession();
- mDrmSessionManager->removeSession(mSessionId2);
+ mDrmSessionManager->removeSession(mTestDrm2->mSessionId);
- const PidSessionInfosMap& map = sessionMap();
- EXPECT_EQ(2u, map.size());
- const SessionInfos& infos1 = map.valueFor(kTestPid1);
- const SessionInfos& infos2 = map.valueFor(kTestPid2);
- EXPECT_EQ(1u, infos1.size());
- EXPECT_EQ(1u, infos2.size());
- // mSessionId2 has been removed.
- ExpectEqSessionInfo(infos2[0], mTestDrm2, mSessionId3, 2);
-}
-
-TEST_F(DrmSessionManagerTest, removeDrm) {
- addSession();
-
- sp<FakeDrm> drm = new FakeDrm;
- const uint8_t ids[] = {123};
- Vector<uint8_t> sessionId;
- GetSessionId(ids, ARRAY_SIZE(ids), &sessionId);
- mDrmSessionManager->addSession(kTestPid2, drm, sessionId);
-
- mDrmSessionManager->removeDrm(mTestDrm2);
-
- const PidSessionInfosMap& map = sessionMap();
- const SessionInfos& infos2 = map.valueFor(kTestPid2);
- EXPECT_EQ(1u, infos2.size());
- // mTestDrm2 has been removed.
- ExpectEqSessionInfo(infos2[0], drm, sessionId, 3);
+ EXPECT_EQ(2u, mDrmSessionManager->getSessionCount());
+ EXPECT_TRUE(mDrmSessionManager->containsSession(mTestDrm1->mSessionId));
+ EXPECT_FALSE(mDrmSessionManager->containsSession(mTestDrm2->mSessionId));
+ EXPECT_TRUE(mDrmSessionManager->containsSession(mTestDrm3->mSessionId));
}
TEST_F(DrmSessionManagerTest, reclaimSession) {
@@ -224,30 +194,63 @@
EXPECT_FALSE(mDrmSessionManager->reclaimSession(50));
EXPECT_TRUE(mDrmSessionManager->reclaimSession(10));
- EXPECT_EQ(1u, mTestDrm1->reclaimedSessions().size());
- EXPECT_TRUE(isEqualSessionId(mSessionId1, mTestDrm1->reclaimedSessions()[0]));
-
- mDrmSessionManager->removeSession(mSessionId1);
+ EXPECT_TRUE(mTestDrm1->isReclaimed());
// add a session from a higher priority process.
- sp<FakeDrm> drm = new FakeDrm;
- const uint8_t ids[] = {1, 3, 5};
- Vector<uint8_t> sessionId;
- GetSessionId(ids, ARRAY_SIZE(ids), &sessionId);
- mDrmSessionManager->addSession(15, drm, sessionId);
+ const std::vector<uint8_t> sid{1, 3, 5};
+ sp<FakeDrm> drm = new FakeDrm(sid, mDrmSessionManager);
+ mDrmSessionManager->addSession(15, drm, drm->mSessionId);
+ // make sure mTestDrm2 is reclaimed next instead of mTestDrm3
+ mDrmSessionManager->useSession(mTestDrm3->mSessionId);
EXPECT_TRUE(mDrmSessionManager->reclaimSession(18));
- EXPECT_EQ(1u, mTestDrm2->reclaimedSessions().size());
- // mSessionId2 is reclaimed.
- EXPECT_TRUE(isEqualSessionId(mSessionId2, mTestDrm2->reclaimedSessions()[0]));
+ EXPECT_TRUE(mTestDrm2->isReclaimed());
+
+ EXPECT_EQ(2u, mDrmSessionManager->getSessionCount());
+ EXPECT_FALSE(mDrmSessionManager->containsSession(mTestDrm1->mSessionId));
+ EXPECT_FALSE(mDrmSessionManager->containsSession(mTestDrm2->mSessionId));
+ EXPECT_TRUE(mDrmSessionManager->containsSession(mTestDrm3->mSessionId));
+ EXPECT_TRUE(mDrmSessionManager->containsSession(drm->mSessionId));
}
-TEST_F(DrmSessionManagerTest, getLowestPriority) {
- testGetLowestPriority();
-}
+TEST_F(DrmSessionManagerTest, reclaimAfterUse) {
+ // nothing to reclaim yet
+ EXPECT_FALSE(mDrmSessionManager->reclaimSession(kTestPid1));
+ EXPECT_FALSE(mDrmSessionManager->reclaimSession(kTestPid2));
-TEST_F(DrmSessionManagerTest, getLeastUsedSession_l) {
- testGetLeastUsedSession();
+ // add sessions from same pid
+ mDrmSessionManager->addSession(kTestPid2, mTestDrm1, mTestDrm1->mSessionId);
+ mDrmSessionManager->addSession(kTestPid2, mTestDrm2, mTestDrm2->mSessionId);
+ mDrmSessionManager->addSession(kTestPid2, mTestDrm3, mTestDrm3->mSessionId);
+
+ // use some but not all sessions
+ mDrmSessionManager->useSession(mTestDrm1->mSessionId);
+ mDrmSessionManager->useSession(mTestDrm1->mSessionId);
+ mDrmSessionManager->useSession(mTestDrm2->mSessionId);
+
+ // calling pid priority is too low
+ int lowPriorityPid = kTestPid2 + 1;
+ EXPECT_FALSE(mDrmSessionManager->reclaimSession(lowPriorityPid));
+
+ // unused session is reclaimed first
+ int highPriorityPid = kTestPid2 - 1;
+ EXPECT_TRUE(mDrmSessionManager->reclaimSession(highPriorityPid));
+ EXPECT_FALSE(mTestDrm1->isReclaimed());
+ EXPECT_FALSE(mTestDrm2->isReclaimed());
+ EXPECT_TRUE(mTestDrm3->isReclaimed());
+ mDrmSessionManager->removeSession(mTestDrm3->mSessionId);
+
+ // less-used session is reclaimed next
+ EXPECT_TRUE(mDrmSessionManager->reclaimSession(highPriorityPid));
+ EXPECT_FALSE(mTestDrm1->isReclaimed());
+ EXPECT_TRUE(mTestDrm2->isReclaimed());
+ EXPECT_TRUE(mTestDrm3->isReclaimed());
+
+ // most-used session still open
+ EXPECT_EQ(1u, mDrmSessionManager->getSessionCount());
+ EXPECT_TRUE(mDrmSessionManager->containsSession(mTestDrm1->mSessionId));
+ EXPECT_FALSE(mDrmSessionManager->containsSession(mTestDrm2->mSessionId));
+ EXPECT_FALSE(mDrmSessionManager->containsSession(mTestDrm3->mSessionId));
}
} // namespace android
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 3d6ebc3..77eace9 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -527,7 +527,7 @@
mFlags(0),
mStickyError(OK),
mSoftRenderer(NULL),
- mAnalyticsItem(NULL),
+ mMetricsHandle(0),
mIsVideo(false),
mVideoWidth(0),
mVideoHeight(0),
@@ -548,19 +548,19 @@
mResourceManagerClient = new ResourceManagerClient(this);
mResourceManagerService = new ResourceManagerServiceProxy(pid, mUid);
- initAnalyticsItem();
+ initMediametrics();
}
MediaCodec::~MediaCodec() {
CHECK_EQ(mState, UNINITIALIZED);
mResourceManagerService->removeClient(getId(mResourceManagerClient));
- flushAnalyticsItem();
+ flushMediametrics();
}
-void MediaCodec::initAnalyticsItem() {
- if (mAnalyticsItem == NULL) {
- mAnalyticsItem = MediaAnalyticsItem::create(kCodecKeyName);
+void MediaCodec::initMediametrics() {
+ if (mMetricsHandle == 0) {
+ mMetricsHandle = mediametrics_create(kCodecKeyName);
}
mLatencyHist.setup(kLatencyHistBuckets, kLatencyHistWidth, kLatencyHistFloor);
@@ -574,38 +574,39 @@
}
}
-void MediaCodec::updateAnalyticsItem() {
- ALOGV("MediaCodec::updateAnalyticsItem");
- if (mAnalyticsItem == NULL) {
+void MediaCodec::updateMediametrics() {
+ ALOGV("MediaCodec::updateMediametrics");
+ if (mMetricsHandle == 0) {
return;
}
+
if (mLatencyHist.getCount() != 0 ) {
- mAnalyticsItem->setInt64(kCodecLatencyMax, mLatencyHist.getMax());
- mAnalyticsItem->setInt64(kCodecLatencyMin, mLatencyHist.getMin());
- mAnalyticsItem->setInt64(kCodecLatencyAvg, mLatencyHist.getAvg());
- mAnalyticsItem->setInt64(kCodecLatencyCount, mLatencyHist.getCount());
+ mediametrics_setInt64(mMetricsHandle, kCodecLatencyMax, mLatencyHist.getMax());
+ mediametrics_setInt64(mMetricsHandle, kCodecLatencyMin, mLatencyHist.getMin());
+ mediametrics_setInt64(mMetricsHandle, kCodecLatencyAvg, mLatencyHist.getAvg());
+ mediametrics_setInt64(mMetricsHandle, kCodecLatencyCount, mLatencyHist.getCount());
if (kEmitHistogram) {
// and the histogram itself
std::string hist = mLatencyHist.emit();
- mAnalyticsItem->setCString(kCodecLatencyHist, hist.c_str());
+ mediametrics_setCString(mMetricsHandle, kCodecLatencyHist, hist.c_str());
}
}
if (mLatencyUnknown > 0) {
- mAnalyticsItem->setInt64(kCodecLatencyUnknown, mLatencyUnknown);
+ mediametrics_setInt64(mMetricsHandle, kCodecLatencyUnknown, mLatencyUnknown);
}
#if 0
// enable for short term, only while debugging
- updateEphemeralAnalytics(mAnalyticsItem);
+ updateEphemeralMediametrics(mMetricsHandle);
#endif
}
-void MediaCodec::updateEphemeralAnalytics(MediaAnalyticsItem *item) {
- ALOGD("MediaCodec::updateEphemeralAnalytics()");
+void MediaCodec::updateEphemeralMediametrics(mediametrics_handle_t item) {
+ ALOGD("MediaCodec::updateEphemeralMediametrics()");
- if (item == NULL) {
+ if (item == 0) {
return;
}
@@ -628,28 +629,27 @@
// spit the data (if any) into the supplied analytics record
if (recentHist.getCount()!= 0 ) {
- item->setInt64(kCodecRecentLatencyMax, recentHist.getMax());
- item->setInt64(kCodecRecentLatencyMin, recentHist.getMin());
- item->setInt64(kCodecRecentLatencyAvg, recentHist.getAvg());
- item->setInt64(kCodecRecentLatencyCount, recentHist.getCount());
+ mediametrics_setInt64(item, kCodecRecentLatencyMax, recentHist.getMax());
+ mediametrics_setInt64(item, kCodecRecentLatencyMin, recentHist.getMin());
+ mediametrics_setInt64(item, kCodecRecentLatencyAvg, recentHist.getAvg());
+ mediametrics_setInt64(item, kCodecRecentLatencyCount, recentHist.getCount());
if (kEmitHistogram) {
// and the histogram itself
std::string hist = recentHist.emit();
- item->setCString(kCodecRecentLatencyHist, hist.c_str());
+ mediametrics_setCString(item, kCodecRecentLatencyHist, hist.c_str());
}
}
}
-void MediaCodec::flushAnalyticsItem() {
- updateAnalyticsItem();
- if (mAnalyticsItem != NULL) {
- // don't log empty records
- if (mAnalyticsItem->count() > 0) {
- mAnalyticsItem->selfrecord();
+void MediaCodec::flushMediametrics() {
+ updateMediametrics();
+ if (mMetricsHandle != 0) {
+ if (mediametrics_count(mMetricsHandle) > 0) {
+ mediametrics_selfRecord(mMetricsHandle);
}
- delete mAnalyticsItem;
- mAnalyticsItem = NULL;
+ mediametrics_delete(mMetricsHandle);
+ mMetricsHandle = 0;
}
}
@@ -981,9 +981,10 @@
// ".secure"
msg->setString("name", name);
- if (mAnalyticsItem != NULL) {
- mAnalyticsItem->setCString(kCodecCodec, name.c_str());
- mAnalyticsItem->setCString(kCodecMode, mIsVideo ? kCodecModeVideo : kCodecModeAudio);
+ if (mMetricsHandle != 0) {
+ mediametrics_setCString(mMetricsHandle, kCodecCodec, name.c_str());
+ mediametrics_setCString(mMetricsHandle, kCodecMode,
+ mIsVideo ? kCodecModeVideo : kCodecModeAudio);
}
if (mIsVideo) {
@@ -1044,16 +1045,17 @@
uint32_t flags) {
sp<AMessage> msg = new AMessage(kWhatConfigure, this);
- if (mAnalyticsItem != NULL) {
+ if (mMetricsHandle != 0) {
int32_t profile = 0;
if (format->findInt32("profile", &profile)) {
- mAnalyticsItem->setInt32(kCodecProfile, profile);
+ mediametrics_setInt32(mMetricsHandle, kCodecProfile, profile);
}
int32_t level = 0;
if (format->findInt32("level", &level)) {
- mAnalyticsItem->setInt32(kCodecLevel, level);
+ mediametrics_setInt32(mMetricsHandle, kCodecLevel, level);
}
- mAnalyticsItem->setInt32(kCodecEncoder, (flags & CONFIGURE_FLAG_ENCODE) ? 1 : 0);
+ mediametrics_setInt32(mMetricsHandle, kCodecEncoder,
+ (flags & CONFIGURE_FLAG_ENCODE) ? 1 : 0);
}
if (mIsVideo) {
@@ -1063,17 +1065,17 @@
mRotationDegrees = 0;
}
- if (mAnalyticsItem != NULL) {
- mAnalyticsItem->setInt32(kCodecWidth, mVideoWidth);
- mAnalyticsItem->setInt32(kCodecHeight, mVideoHeight);
- mAnalyticsItem->setInt32(kCodecRotation, mRotationDegrees);
+ if (mMetricsHandle != 0) {
+ mediametrics_setInt32(mMetricsHandle, kCodecWidth, mVideoWidth);
+ mediametrics_setInt32(mMetricsHandle, kCodecHeight, mVideoHeight);
+ mediametrics_setInt32(mMetricsHandle, kCodecRotation, mRotationDegrees);
int32_t maxWidth = 0;
if (format->findInt32("max-width", &maxWidth)) {
- mAnalyticsItem->setInt32(kCodecMaxWidth, maxWidth);
+ mediametrics_setInt32(mMetricsHandle, kCodecMaxWidth, maxWidth);
}
int32_t maxHeight = 0;
if (format->findInt32("max-height", &maxHeight)) {
- mAnalyticsItem->setInt32(kCodecMaxHeight, maxHeight);
+ mediametrics_setInt32(mMetricsHandle, kCodecMaxHeight, maxHeight);
}
}
@@ -1095,8 +1097,8 @@
} else {
msg->setPointer("descrambler", descrambler.get());
}
- if (mAnalyticsItem != NULL) {
- mAnalyticsItem->setInt32(kCodecCrypto, 1);
+ if (mMetricsHandle != 0) {
+ mediametrics_setInt32(mMetricsHandle, kCodecCrypto, 1);
}
} else if (mFlags & kFlagIsSecure) {
ALOGW("Crypto or descrambler should be given for secure codec");
@@ -1561,22 +1563,22 @@
return OK;
}
-status_t MediaCodec::getMetrics(MediaAnalyticsItem * &reply) {
+status_t MediaCodec::getMetrics(mediametrics_handle_t &reply) {
- reply = NULL;
+ reply = 0;
// shouldn't happen, but be safe
- if (mAnalyticsItem == NULL) {
+ if (mMetricsHandle == 0) {
return UNKNOWN_ERROR;
}
// update any in-flight data that's not carried within the record
- updateAnalyticsItem();
+ updateMediametrics();
// send it back to the caller.
- reply = mAnalyticsItem->dup();
+ reply = mediametrics_dup(mMetricsHandle);
- updateEphemeralAnalytics(reply);
+ updateEphemeralMediametrics(reply);
return OK;
}
@@ -1890,10 +1892,11 @@
case CONFIGURING:
{
if (actionCode == ACTION_CODE_FATAL) {
- mAnalyticsItem->setInt32(kCodecError, err);
- mAnalyticsItem->setCString(kCodecErrorState, stateString(mState).c_str());
- flushAnalyticsItem();
- initAnalyticsItem();
+ mediametrics_setInt32(mMetricsHandle, kCodecError, err);
+ mediametrics_setCString(mMetricsHandle, kCodecErrorState,
+ stateString(mState).c_str());
+ flushMediametrics();
+ initMediametrics();
}
setState(actionCode == ACTION_CODE_FATAL ?
UNINITIALIZED : INITIALIZED);
@@ -1903,10 +1906,11 @@
case STARTING:
{
if (actionCode == ACTION_CODE_FATAL) {
- mAnalyticsItem->setInt32(kCodecError, err);
- mAnalyticsItem->setCString(kCodecErrorState, stateString(mState).c_str());
- flushAnalyticsItem();
- initAnalyticsItem();
+ mediametrics_setInt32(mMetricsHandle, kCodecError, err);
+ mediametrics_setCString(mMetricsHandle, kCodecErrorState,
+ stateString(mState).c_str());
+ flushMediametrics();
+ initMediametrics();
}
setState(actionCode == ACTION_CODE_FATAL ?
UNINITIALIZED : CONFIGURED);
@@ -1944,10 +1948,11 @@
case FLUSHING:
{
if (actionCode == ACTION_CODE_FATAL) {
- mAnalyticsItem->setInt32(kCodecError, err);
- mAnalyticsItem->setCString(kCodecErrorState, stateString(mState).c_str());
- flushAnalyticsItem();
- initAnalyticsItem();
+ mediametrics_setInt32(mMetricsHandle, kCodecError, err);
+ mediametrics_setCString(mMetricsHandle, kCodecErrorState,
+ stateString(mState).c_str());
+ flushMediametrics();
+ initMediametrics();
setState(UNINITIALIZED);
} else {
@@ -1977,10 +1982,11 @@
setState(INITIALIZED);
break;
default:
- mAnalyticsItem->setInt32(kCodecError, err);
- mAnalyticsItem->setCString(kCodecErrorState, stateString(mState).c_str());
- flushAnalyticsItem();
- initAnalyticsItem();
+ mediametrics_setInt32(mMetricsHandle, kCodecError, err);
+ mediametrics_setCString(mMetricsHandle, kCodecErrorState,
+ stateString(mState).c_str());
+ flushMediametrics();
+ initMediametrics();
setState(UNINITIALIZED);
break;
}
@@ -2037,7 +2043,8 @@
CHECK(msg->findString("componentName", &mComponentName));
if (mComponentName.c_str()) {
- mAnalyticsItem->setCString(kCodecCodec, mComponentName.c_str());
+ mediametrics_setCString(mMetricsHandle, kCodecCodec,
+ mComponentName.c_str());
}
const char *owner = mCodecInfo->getOwnerName();
@@ -2053,11 +2060,11 @@
if (mComponentName.endsWith(".secure")) {
mFlags |= kFlagIsSecure;
resourceType = MediaResource::kSecureCodec;
- mAnalyticsItem->setInt32(kCodecSecure, 1);
+ mediametrics_setInt32(mMetricsHandle, kCodecSecure, 1);
} else {
mFlags &= ~kFlagIsSecure;
resourceType = MediaResource::kNonSecureCodec;
- mAnalyticsItem->setInt32(kCodecSecure, 0);
+ mediametrics_setInt32(mMetricsHandle, kCodecSecure, 0);
}
if (mIsVideo) {
@@ -2105,14 +2112,15 @@
(new AMessage)->postReply(mReplyID);
// augment our media metrics info, now that we know more things
- if (mAnalyticsItem != NULL) {
+ if (mMetricsHandle != 0) {
sp<AMessage> format;
if (mConfigureMsg != NULL &&
mConfigureMsg->findMessage("format", &format)) {
// format includes: mime
AString mime;
if (format->findString("mime", &mime)) {
- mAnalyticsItem->setCString(kCodecMime, mime.c_str());
+ mediametrics_setCString(mMetricsHandle, kCodecMime,
+ mime.c_str());
}
}
}
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/MediaCodec.h b/media/libstagefright/include/media/stagefright/MediaCodec.h
index cd30347..01d0325 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodec.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodec.h
@@ -25,7 +25,7 @@
#include <media/hardware/CryptoAPI.h>
#include <media/MediaCodecInfo.h>
#include <media/MediaResource.h>
-#include <media/MediaAnalyticsItem.h>
+#include <media/MediaMetrics.h>
#include <media/stagefright/foundation/AHandler.h>
#include <media/stagefright/FrameRenderTracker.h>
#include <utils/Vector.h>
@@ -189,7 +189,7 @@
status_t getCodecInfo(sp<MediaCodecInfo> *codecInfo) const;
- status_t getMetrics(MediaAnalyticsItem * &reply);
+ status_t getMetrics(mediametrics_handle_t &reply);
status_t setParameters(const sp<AMessage> ¶ms);
@@ -328,11 +328,11 @@
sp<Surface> mSurface;
SoftwareRenderer *mSoftRenderer;
- MediaAnalyticsItem *mAnalyticsItem;
- void initAnalyticsItem();
- void updateAnalyticsItem();
- void flushAnalyticsItem();
- void updateEphemeralAnalytics(MediaAnalyticsItem *item);
+ mediametrics_handle_t mMetricsHandle;
+ void initMediametrics();
+ void updateMediametrics();
+ void flushMediametrics();
+ void updateEphemeralMediametrics(mediametrics_handle_t item);
sp<AMessage> mOutputFormat;
sp<AMessage> mInputFormat;
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/README.md b/media/tests/benchmark/README.md
index 6627462..05fbe6f 100644
--- a/media/tests/benchmark/README.md
+++ b/media/tests/benchmark/README.md
@@ -145,3 +145,12 @@
```
adb shell /data/local/tmp/C2DecoderTest -P /data/local/tmp/MediaBenchmark/res/
```
+## C2 Encoder
+
+The test encodes input stream and benchmarks the codec2 encoders available in device.
+
+Setup steps are same as [extractor](#extractor).
+
+```
+adb shell /data/local/tmp/C2EncoderTest -P /data/local/tmp/MediaBenchmark/res/
+```
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 8153a86..c11fe36 100644
--- a/media/tests/benchmark/src/native/common/BenchmarkCommon.h
+++ b/media/tests/benchmark/src/native/common/BenchmarkCommon.h
@@ -17,15 +17,18 @@
#ifndef __BENCHMARK_COMMON_H__
#define __BENCHMARK_COMMON_H__
+#include <sys/stat.h>
#include <inttypes.h>
#include <mutex>
#include <queue>
#include <thread>
+#include <iostream>
#include <media/NdkMediaCodec.h>
#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 239f378..8de7823 100644
--- a/media/tests/benchmark/src/native/encoder/Android.bp
+++ b/media/tests/benchmark/src/native/encoder/Android.bp
@@ -23,11 +23,31 @@
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_codec2_common-defaults",
+ ],
+
+ srcs: ["C2Encoder.cpp"],
+
+ static_libs: [
+ "libmediabenchmark_codec2_common",
+ "libmediabenchmark_codec2_extractor",
+ "libmediabenchmark_codec2_decoder",
+ ],
+
+ export_include_dirs: ["."],
+
+ ldflags: ["-Wl,-Bsymbolic"],
}
diff --git a/media/tests/benchmark/src/native/encoder/C2Encoder.cpp b/media/tests/benchmark/src/native/encoder/C2Encoder.cpp
new file mode 100644
index 0000000..33429ef
--- /dev/null
+++ b/media/tests/benchmark/src/native/encoder/C2Encoder.cpp
@@ -0,0 +1,264 @@
+/*
+ * 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 "C2Encoder"
+
+#include "C2Encoder.h"
+
+int32_t C2Encoder::createCodec2Component(string compName, AMediaFormat *format) {
+ ALOGV("In %s", __func__);
+ mListener.reset(new CodecListener(
+ [this](std::list<std::unique_ptr<C2Work>> &workItems) { handleWorkDone(workItems); }));
+ if (!mListener) return -1;
+
+ const char *mime = nullptr;
+ AMediaFormat_getString(format, AMEDIAFORMAT_KEY_MIME, &mime);
+ if (!mime) {
+ ALOGE("Error in AMediaFormat_getString");
+ return -1;
+ }
+ // Configure the plugin with Input properties
+ std::vector<C2Param *> configParam;
+ if (!strncmp(mime, "audio/", 6)) {
+ mIsAudioEncoder = true;
+ int32_t numChannels;
+ if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_SAMPLE_RATE, &mSampleRate)) {
+ ALOGE("AMEDIAFORMAT_KEY_SAMPLE_RATE not set");
+ return -1;
+ }
+ if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_CHANNEL_COUNT, &numChannels)) {
+ ALOGE("AMEDIAFORMAT_KEY_CHANNEL_COUNT not set");
+ return -1;
+ }
+ C2StreamSampleRateInfo::input sampleRateInfo(0u, mSampleRate);
+ C2StreamChannelCountInfo::input channelCountInfo(0u, numChannels);
+ configParam.push_back(&sampleRateInfo);
+ configParam.push_back(&channelCountInfo);
+ } else {
+ mIsAudioEncoder = false;
+ if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_WIDTH, &mWidth)) {
+ ALOGE("AMEDIAFORMAT_KEY_WIDTH not set");
+ return -1;
+ }
+ if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_HEIGHT, &mHeight)) {
+ ALOGE("AMEDIAFORMAT_KEY_HEIGHT not set");
+ return -1;
+ }
+ C2StreamPictureSizeInfo::input inputSize(0u, mWidth, mHeight);
+ configParam.push_back(&inputSize);
+
+ if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_FRAME_RATE, &mFrameRate) ||
+ (mFrameRate <= 0)) {
+ mFrameRate = KDefaultFrameRate;
+ }
+ }
+
+ int64_t sTime = mStats->getCurTime();
+ mComponent = mClient->CreateComponentByName(compName.c_str(), mListener, &mClient);
+ if (mComponent == nullptr) {
+ ALOGE("Create component failed for %s", compName.c_str());
+ return -1;
+ }
+ std::vector<std::unique_ptr<C2SettingResult>> failures;
+ int32_t status = mComponent->config(configParam, C2_DONT_BLOCK, &failures);
+ if (failures.size() != 0) {
+ ALOGE("Invalid Configuration");
+ return -1;
+ }
+
+ status |= mComponent->start();
+ int64_t eTime = mStats->getCurTime();
+ int64_t timeTaken = mStats->getTimeDiff(sTime, eTime);
+ mStats->setInitTime(timeTaken);
+ return status;
+}
+
+// In encoder components, fetch the size of input buffer allocated
+int32_t C2Encoder::getInputMaxBufSize() {
+ int32_t bitStreamInfo[1] = {0};
+ std::vector<std::unique_ptr<C2Param>> inParams;
+ c2_status_t status = mComponent->query({}, {C2StreamMaxBufferSizeInfo::input::PARAM_TYPE},
+ C2_DONT_BLOCK, &inParams);
+ if (status != C2_OK && inParams.size() == 0) {
+ ALOGE("Query MaxBufferSizeInfo failed => %d", status);
+ return status;
+ } else {
+ size_t offset = sizeof(C2Param);
+ for (size_t i = 0; i < inParams.size(); ++i) {
+ C2Param *param = inParams[i].get();
+ bitStreamInfo[i] = *(int32_t *)((uint8_t *)param + offset);
+ }
+ }
+ mInputMaxBufSize = bitStreamInfo[0];
+ if (mInputMaxBufSize < 0) {
+ ALOGE("Invalid mInputMaxBufSize %d\n", mInputMaxBufSize);
+ return -1;
+ }
+ return status;
+}
+
+int32_t C2Encoder::encodeFrames(ifstream &eleStream, size_t inputBufferSize) {
+ ALOGV("In %s", __func__);
+ int32_t frameSize = 0;
+ if (!mIsAudioEncoder) {
+ frameSize = mWidth * mHeight * 3 / 2;
+ } else {
+ frameSize = DEFAULT_AUDIO_FRAME_SIZE;
+ if (getInputMaxBufSize() != 0) return -1;
+ if (frameSize > mInputMaxBufSize) {
+ frameSize = mInputMaxBufSize;
+ }
+ }
+ int32_t numFrames = (inputBufferSize + frameSize - 1) / frameSize;
+ // Temporary buffer to read data from the input file
+ char *data = (char *)malloc(frameSize);
+ if (!data) {
+ ALOGE("Insufficient memory to read from input file");
+ return -1;
+ }
+
+ typedef std::unique_lock<std::mutex> ULock;
+ uint64_t presentationTimeUs = 0;
+ size_t offset = 0;
+ c2_status_t status = C2_OK;
+
+ mStats->setStartTime();
+ while (numFrames > 0) {
+ std::unique_ptr<C2Work> work;
+ // Prepare C2Work
+ {
+ ULock l(mQueueLock);
+ if (mWorkQueue.empty()) mQueueCondition.wait_for(l, MAX_RETRY * TIME_OUT);
+ if (!mWorkQueue.empty()) {
+ mStats->addInputTime();
+ work.swap(mWorkQueue.front());
+ mWorkQueue.pop_front();
+ } else {
+ cout << "Wait for generating C2Work exceeded timeout" << endl;
+ return -1;
+ }
+ }
+
+ if (mIsAudioEncoder) {
+ presentationTimeUs = mNumInputFrame * frameSize * (1000000 / mSampleRate);
+ } else {
+ presentationTimeUs = mNumInputFrame * (1000000 / mFrameRate);
+ }
+ uint32_t flags = 0;
+ if (numFrames == 1) flags |= C2FrameData::FLAG_END_OF_STREAM;
+
+ work->input.flags = (C2FrameData::flags_t)flags;
+ work->input.ordinal.timestamp = presentationTimeUs;
+ work->input.ordinal.frameIndex = mNumInputFrame;
+ work->input.buffers.clear();
+
+ if (inputBufferSize - offset < frameSize) {
+ frameSize = inputBufferSize - offset;
+ }
+ eleStream.read(data, frameSize);
+ if (eleStream.gcount() != frameSize) {
+ ALOGE("read() from file failed. Incorrect bytes read");
+ return -1;
+ }
+ offset += frameSize;
+
+ if (frameSize) {
+ if (mIsAudioEncoder) {
+ std::shared_ptr<C2LinearBlock> block;
+ status = mLinearPool->fetchLinearBlock(
+ frameSize, {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE}, &block);
+ if (status != C2_OK || !block) {
+ cout << "fetchLinearBlock failed : " << status << endl;
+ return status;
+ }
+ C2WriteView view = block->map().get();
+ if (view.error() != C2_OK) {
+ cout << "C2LinearBlock::map() failed : " << view.error() << endl;
+ return view.error();
+ }
+
+ memcpy(view.base(), data, frameSize);
+ work->input.buffers.emplace_back(new LinearBuffer(block));
+ } else {
+ std::shared_ptr<C2GraphicBlock> block;
+ status = mGraphicPool->fetchGraphicBlock(
+ mWidth, mHeight, HAL_PIXEL_FORMAT_YV12,
+ {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE}, &block);
+ if (status != C2_OK || !block) {
+ cout << "fetchGraphicBlock failed : " << status << endl;
+ return status;
+ }
+ C2GraphicView view = block->map().get();
+ if (view.error() != C2_OK) {
+ cout << "C2GraphicBlock::map() failed : " << view.error() << endl;
+ return view.error();
+ }
+
+ uint8_t *pY = view.data()[C2PlanarLayout::PLANE_Y];
+ uint8_t *pU = view.data()[C2PlanarLayout::PLANE_U];
+ uint8_t *pV = view.data()[C2PlanarLayout::PLANE_V];
+ memcpy(pY, data, mWidth * mHeight);
+ memcpy(pU, data + mWidth * mHeight, (mWidth * mHeight >> 2));
+ memcpy(pV, data + (mWidth * mHeight * 5 >> 2), mWidth * mHeight >> 2);
+ work->input.buffers.emplace_back(new GraphicBuffer(block));
+ }
+ mStats->addFrameSize(frameSize);
+ }
+
+ work->worklets.clear();
+ work->worklets.emplace_back(new C2Worklet);
+
+ std::list<std::unique_ptr<C2Work>> items;
+ items.push_back(std::move(work));
+ // queue() invokes process() function of C2 Plugin.
+ status = mComponent->queue(&items);
+ if (status != C2_OK) {
+ ALOGE("queue failed");
+ return status;
+ }
+ ALOGV("Frame #%d size = %d queued", mNumInputFrame, frameSize);
+ numFrames--;
+ mNumInputFrame++;
+ }
+ free(data);
+ return status;
+}
+
+void C2Encoder::deInitCodec() {
+ ALOGV("In %s", __func__);
+ if (!mComponent) return;
+
+ int64_t sTime = mStats->getCurTime();
+ mComponent->stop();
+ mComponent->release();
+ mComponent = nullptr;
+ int64_t eTime = mStats->getCurTime();
+ int64_t timeTaken = mStats->getTimeDiff(sTime, eTime);
+ mStats->setDeInitTime(timeTaken);
+}
+
+void C2Encoder::dumpStatistics(string inputReference, int64_t durationUs) {
+ string operation = "c2encode";
+ mStats->dumpStatistics(operation, inputReference, durationUs);
+}
+
+void C2Encoder::resetEncoder() {
+ mIsAudioEncoder = false;
+ mNumInputFrame = 0;
+ mEos = false;
+ if (mStats) mStats->reset();
+}
diff --git a/media/tests/benchmark/src/native/encoder/C2Encoder.h b/media/tests/benchmark/src/native/encoder/C2Encoder.h
new file mode 100644
index 0000000..a4ca097
--- /dev/null
+++ b/media/tests/benchmark/src/native/encoder/C2Encoder.h
@@ -0,0 +1,66 @@
+/*
+ * 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 __C2_ENCODER_H__
+#define __C2_ENCODER_H__
+
+#include <stdio.h>
+#include <algorithm>
+#include <fstream>
+
+#include "BenchmarkC2Common.h"
+
+#define DEFAULT_AUDIO_FRAME_SIZE 4096
+
+constexpr int32_t KDefaultFrameRate = 25;
+
+class C2Encoder : public BenchmarkC2Common {
+ public:
+ C2Encoder()
+ : mIsAudioEncoder(false),
+ mWidth(0),
+ mHeight(0),
+ mNumInputFrame(0),
+ mComponent(nullptr) {}
+
+ int32_t createCodec2Component(string codecName, AMediaFormat *format);
+
+ int32_t encodeFrames(ifstream &eleStream, size_t inputBufferSize);
+
+ int32_t getInputMaxBufSize();
+
+ void deInitCodec();
+
+ void dumpStatistics(string inputReference, int64_t durationUs);
+
+ void resetEncoder();
+
+ private:
+ bool mIsAudioEncoder;
+
+ int32_t mWidth;
+ int32_t mHeight;
+ int32_t mFrameRate;
+ int32_t mSampleRate;
+
+ int32_t mNumInputFrame;
+ int32_t mInputMaxBufSize;
+
+ std::shared_ptr<android::Codec2Client::Listener> mListener;
+ std::shared_ptr<android::Codec2Client::Component> mComponent;
+};
+
+#endif // __C2_ENCODER_H__
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 128d055..f46fa4a 100644
--- a/media/tests/benchmark/tests/Android.bp
+++ b/media/tests/benchmark/tests/Android.bp
@@ -87,8 +87,25 @@
srcs: ["C2DecoderTest.cpp"],
static_libs: [
- "libmediabenchmark_extractor",
+ "libmediabenchmark_codec2_extractor",
"libmediabenchmark_codec2_common",
"libmediabenchmark_codec2_decoder",
],
}
+
+cc_test {
+ name: "C2EncoderTest",
+ gtest: true,
+ defaults: [
+ "libmediabenchmark_codec2_common-defaults",
+ ],
+
+ srcs: ["C2EncoderTest.cpp"],
+
+ static_libs: [
+ "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
new file mode 100644
index 0000000..98eb17a
--- /dev/null
+++ b/media/tests/benchmark/tests/C2EncoderTest.cpp
@@ -0,0 +1,187 @@
+/*
+ * 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 "C2EncoderTest"
+
+#include <fstream>
+#include <iostream>
+#include <limits>
+
+#include "BenchmarkTestEnvironment.h"
+#include "C2Encoder.h"
+#include "Decoder.h"
+
+static BenchmarkTestEnvironment *gEnv = nullptr;
+
+class C2EncoderTest : public ::testing::TestWithParam<pair<string, string>> {
+ public:
+ C2EncoderTest() : mEncoder(nullptr) {}
+
+ ~C2EncoderTest() {
+ if (!mCodecList.empty()) {
+ mCodecList.clear();
+ }
+ if (mEncoder) {
+ delete mEncoder;
+ mEncoder = nullptr;
+ }
+ }
+
+ virtual void SetUp() override { setupC2EncoderTest(); }
+
+ void setupC2EncoderTest();
+
+ vector<string> mCodecList;
+ C2Encoder *mEncoder;
+};
+
+void C2EncoderTest::setupC2EncoderTest() {
+ mEncoder = new C2Encoder();
+ ASSERT_NE(mEncoder, nullptr) << "C2Encoder creation failed";
+
+ int32_t status = mEncoder->setupCodec2();
+ ASSERT_EQ(status, 0) << "Codec2 setup failed";
+
+ mCodecList = mEncoder->getSupportedComponentList(true /* isEncoder*/);
+ ASSERT_GT(mCodecList.size(), 0) << "Codec2 client didn't recognise any component";
+}
+
+TEST_P(C2EncoderTest, Codec2Encode) {
+ ALOGV("Encodes the input using codec2 framework");
+ string inputFile = gEnv->getRes() + GetParam().first;
+ FILE *inputFp = fopen(inputFile.c_str(), "rb");
+ ASSERT_NE(inputFp, nullptr) << "Unable to open input file for reading";
+
+ Decoder *decoder = new Decoder();
+ ASSERT_NE(decoder, nullptr) << "Decoder creation failed";
+
+ Extractor *extractor = decoder->getExtractor();
+ ASSERT_NE(extractor, nullptr) << "Extractor creation failed";
+
+ // Read file properties
+ struct stat buf;
+ stat(inputFile.c_str(), &buf);
+ size_t fileSize = buf.st_size;
+ int32_t fd = fileno(inputFp);
+
+ ASSERT_LE(fileSize, kMaxBufferSize)
+ << "Input file size is greater than the threshold memory dedicated to the test";
+
+ int32_t trackCount = extractor->initExtractor(fd, fileSize);
+ ASSERT_GT(trackCount, 0) << "initExtractor failed";
+
+ for (int curTrack = 0; curTrack < trackCount; curTrack++) {
+ int32_t status = extractor->setupTrackFormat(curTrack);
+ ASSERT_EQ(status, 0) << "Track Format invalid";
+
+ uint8_t *inputBuffer = (uint8_t *)malloc(fileSize);
+ ASSERT_NE(inputBuffer, nullptr) << "Insufficient memory";
+
+ 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
+ ASSERT_LE(inputBufferOffset + info.size, fileSize) << "Memory allocated not sufficient";
+
+ memcpy(inputBuffer + inputBufferOffset, extractor->getFrameBuf(), info.size);
+ frameInfo.push_back(info);
+ inputBufferOffset += info.size;
+ }
+
+ string decName = "";
+ string outputFileName = "decode.out";
+ FILE *outFp = fopen(outputFileName.c_str(), "wb");
+ 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);
+ ASSERT_EQ(status, AMEDIA_OK) << "Decode returned error : " << status;
+
+ // Encode the given input stream for all C2 codecs supported by device
+ AMediaFormat *format = extractor->getFormat();
+ ifstream eleStream;
+ eleStream.open(outputFileName.c_str(), ifstream::binary | ifstream::ate);
+ ASSERT_EQ(eleStream.is_open(), true) << outputFileName.c_str() << " - file not found";
+ size_t eleSize = eleStream.tellg();
+
+ for (string codecName : mCodecList) {
+ if (codecName.find(GetParam().second) != string::npos) {
+ status = mEncoder->createCodec2Component(codecName, format);
+ ASSERT_EQ(status, 0) << "Create component failed for " << codecName;
+
+ // Send the inputs to C2 Encoder and wait till all buffers are returned.
+ eleStream.seekg(0, ifstream::beg);
+ status = mEncoder->encodeFrames(eleStream, eleSize);
+ ASSERT_EQ(status, 0) << "Encoder failed for " << codecName;
+
+ mEncoder->waitOnInputConsumption();
+ ASSERT_TRUE(mEncoder->mEos) << "Test Failed. Didn't receive EOS \n";
+
+ mEncoder->deInitCodec();
+ int64_t durationUs = extractor->getClipDuration();
+ ALOGV("codec : %s", codecName.c_str());
+ mEncoder->dumpStatistics(GetParam().first, durationUs);
+ mEncoder->resetEncoder();
+ }
+ }
+
+ // Destroy the decoder for the given input
+ decoder->deInitCodec();
+ decoder->resetDecoder();
+ free(inputBuffer);
+ }
+ fclose(inputFp);
+ 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")));
+
+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")));
+
+int main(int argc, char **argv) {
+ gEnv = new BenchmarkTestEnvironment();
+ ::testing::AddGlobalTestEnvironment(gEnv);
+ ::testing::InitGoogleTest(&argc, argv);
+ int status = gEnv->initFromOptions(argc, argv);
+ if (status == 0) {
+ status = RUN_ALL_TESTS();
+ ALOGV("C2 Encoder Test result = %d\n", status);
+ }
+ return status;
+}
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/ProcessInfo.cpp b/media/utils/ProcessInfo.cpp
index 27f1a79..113e4a7 100644
--- a/media/utils/ProcessInfo.cpp
+++ b/media/utils/ProcessInfo.cpp
@@ -23,6 +23,7 @@
#include <binder/IPCThreadState.h>
#include <binder/IProcessInfoService.h>
#include <binder/IServiceManager.h>
+#include <private/android_filesystem_config.h>
namespace android {
@@ -55,8 +56,9 @@
bool ProcessInfo::isValidPid(int pid) {
int callingPid = IPCThreadState::self()->getCallingPid();
+ int callingUid = IPCThreadState::self()->getCallingUid();
// Trust it if this is called from the same process otherwise pid has to match the calling pid.
- return (callingPid == getpid()) || (callingPid == pid);
+ return (callingPid == getpid()) || (callingPid == pid) || (callingUid == AID_MEDIA);
}
ProcessInfo::~ProcessInfo() {}
diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp
index ee7a6d6..bbd0d2c 100644
--- a/services/audioflinger/Effects.cpp
+++ b/services/audioflinger/Effects.cpp
@@ -622,7 +622,7 @@
mConfig.outputCfg.format = EFFECT_BUFFER_FORMAT;
// Don't use sample rate for thread if effect isn't offloadable.
- if ((thread->type() == ThreadBase::OFFLOAD) && !isOffloaded()) {
+ if (isOffloadedOrDirect() && !isOffloaded()) {
mConfig.inputCfg.samplingRate = DEFAULT_OUTPUT_SAMPLE_RATE;
ALOGV("Overriding effect input as 48kHz");
} else {
@@ -1856,12 +1856,13 @@
}
// copy to local memory in case of client corruption b/32220769
- param = (effect_param_t *)realloc(param, size);
- if (param == NULL) {
+ auto *newParam = (effect_param_t *)realloc(param, size);
+ if (newParam == NULL) {
ALOGW("command(): out of memory");
status = NO_MEMORY;
break;
}
+ param = newParam;
memcpy(param, p, size);
int reply = 0;
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 23c0a12..b427794 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -1355,6 +1355,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);
@@ -5283,7 +5291,8 @@
auto attr = mEngine->getAllAttributesForProductStrategy(productStrategy).front();
if ((hasVoiceStream(streams) &&
- (isInCall() || mOutputs.isStrategyActiveOnSameModule(productStrategy, outputDesc))) ||
+ (isInCall() || mOutputs.isStrategyActiveOnSameModule(productStrategy, outputDesc)) &&
+ !isStreamActive(AUDIO_STREAM_ENFORCED_AUDIBLE, 0)) ||
((hasStream(streams, AUDIO_STREAM_ALARM) || hasStream(streams, AUDIO_STREAM_ENFORCED_AUDIBLE)) &&
mOutputs.isStrategyActiveOnSameModule(productStrategy, outputDesc)) ||
outputDesc->isStrategyActive(productStrategy)) {
diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
index 47a103b..c1190be 100644
--- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
+++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
@@ -351,12 +351,17 @@
return NO_INIT;
}
+ audio_source_t inputSource = attr->source;
+ if (inputSource == AUDIO_SOURCE_DEFAULT) {
+ inputSource = AUDIO_SOURCE_MIC;
+ }
+
// already checked by client, but double-check in case the client wrapper is bypassed
- if ((attr->source < AUDIO_SOURCE_DEFAULT)
- || (attr->source >= AUDIO_SOURCE_CNT
- && attr->source != AUDIO_SOURCE_HOTWORD
- && attr->source != AUDIO_SOURCE_FM_TUNER
- && attr->source != AUDIO_SOURCE_ECHO_REFERENCE)) {
+ if ((inputSource < AUDIO_SOURCE_DEFAULT)
+ || (inputSource >= AUDIO_SOURCE_CNT
+ && inputSource != AUDIO_SOURCE_HOTWORD
+ && inputSource != AUDIO_SOURCE_FM_TUNER
+ && inputSource != AUDIO_SOURCE_ECHO_REFERENCE)) {
return BAD_VALUE;
}
@@ -385,16 +390,16 @@
}
bool canCaptureOutput = captureAudioOutputAllowed(pid, uid);
- if ((attr->source == AUDIO_SOURCE_VOICE_UPLINK ||
- attr->source == AUDIO_SOURCE_VOICE_DOWNLINK ||
- attr->source == AUDIO_SOURCE_VOICE_CALL ||
- attr->source == AUDIO_SOURCE_ECHO_REFERENCE) &&
+ if ((inputSource == AUDIO_SOURCE_VOICE_UPLINK ||
+ inputSource == AUDIO_SOURCE_VOICE_DOWNLINK ||
+ inputSource == AUDIO_SOURCE_VOICE_CALL ||
+ inputSource == AUDIO_SOURCE_ECHO_REFERENCE) &&
!canCaptureOutput) {
return PERMISSION_DENIED;
}
bool canCaptureHotword = captureHotwordAllowed(opPackageName, pid, uid);
- if ((attr->source == AUDIO_SOURCE_HOTWORD) && !canCaptureHotword) {
+ if ((inputSource == AUDIO_SOURCE_HOTWORD) && !canCaptureHotword) {
return BAD_VALUE;
}
@@ -459,7 +464,7 @@
if (audioPolicyEffects != 0) {
// create audio pre processors according to input source
- status_t status = audioPolicyEffects->addInputEffects(*input, attr->source, session);
+ status_t status = audioPolicyEffects->addInputEffects(*input, inputSource, session);
if (status != NO_ERROR && status != ALREADY_EXISTS) {
ALOGW("Failed to add effects on input %d", *input);
}
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index 4227a3b..bda35f3 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -2165,7 +2165,9 @@
}
void Camera3Device::pauseStateNotify(bool enable) {
- Mutex::Autolock il(mInterfaceLock);
+ // We must not hold mInterfaceLock here since this function is called from
+ // RequestThread::threadLoop and holding mInterfaceLock could lead to
+ // deadlocks (http://b/143513518)
Mutex::Autolock l(mLock);
mPauseStateNotify = enable;
@@ -2742,7 +2744,9 @@
ATRACE_CALL();
bool ret = false;
- Mutex::Autolock il(mInterfaceLock);
+ // We must not hold mInterfaceLock here since this function is called from
+ // RequestThread::threadLoop and holding mInterfaceLock could lead to
+ // deadlocks (http://b/143513518)
nsecs_t maxExpectedDuration = getExpectedInFlightDuration();
Mutex::Autolock l(mLock);
@@ -5371,6 +5375,9 @@
bool Camera3Device::RequestThread::threadLoop() {
ATRACE_CALL();
status_t res;
+ // Any function called from threadLoop() must not hold mInterfaceLock since
+ // it could lead to deadlocks (disconnect() -> hold mInterfaceMutex -> wait for request thread
+ // to finish -> request thread waits on mInterfaceMutex) http://b/143513518
// Handle paused state.
if (waitIfPaused()) {
diff --git a/services/mediadrm/Android.mk b/services/mediadrm/Android.mk
index 9f03964..72d42ae 100644
--- a/services/mediadrm/Android.mk
+++ b/services/mediadrm/Android.mk
@@ -27,6 +27,7 @@
LOCAL_SHARED_LIBRARIES:= \
libbinder \
liblog \
+ libmedia \
libmediadrm \
libutils \
libhidlbase \
diff --git a/services/mediaresourcemanager/Android.bp b/services/mediaresourcemanager/Android.bp
index f3339a0..d468406 100644
--- a/services/mediaresourcemanager/Android.bp
+++ b/services/mediaresourcemanager/Android.bp
@@ -23,4 +23,6 @@
"-Wall",
],
+ export_include_dirs: ["."],
+
}
diff --git a/services/mediaresourcemanager/ResourceManagerService.cpp b/services/mediaresourcemanager/ResourceManagerService.cpp
index bdcd5e4..45eea0f 100644
--- a/services/mediaresourcemanager/ResourceManagerService.cpp
+++ b/services/mediaresourcemanager/ResourceManagerService.cpp
@@ -290,6 +290,18 @@
}
}
+void ResourceManagerService::mergeResources(
+ MediaResource& r1, const MediaResource& r2) {
+ if (r1.mType == MediaResource::kDrmSession) {
+ // This means we are using a session. Each session's mValue is initialized to UINT64_MAX.
+ // The oftener a session is used the lower it's mValue. During reclaim the session with
+ // the highest mValue/lowest usage would be closed.
+ r1.mValue -= (r1.mValue == 0 ? 0 : 1);
+ } else {
+ r1.mValue += r2.mValue;
+ }
+}
+
void ResourceManagerService::addResource(
int pid,
int uid,
@@ -309,15 +321,16 @@
ResourceInfo& info = getResourceInfoForEdit(uid, clientId, client, infos);
for (size_t i = 0; i < resources.size(); ++i) {
- const auto resType = std::make_pair(resources[i].mType, resources[i].mSubType);
+ const auto &res = resources[i];
+ const auto resType = std::tuple(res.mType, res.mSubType, res.mId);
if (info.resources.find(resType) == info.resources.end()) {
- onFirstAdded(resources[i], info);
- info.resources[resType] = resources[i];
+ onFirstAdded(res, info);
+ info.resources[resType] = res;
} else {
- info.resources[resType].mValue += resources[i].mValue;
+ mergeResources(info.resources[resType], res);
}
}
- if (info.deathNotifier == nullptr) {
+ if (info.deathNotifier == nullptr && client != nullptr) {
info.deathNotifier = new DeathNotifier(this, pid, clientId);
IInterface::asBinder(client)->linkToDeath(info.deathNotifier);
}
@@ -351,14 +364,17 @@
ResourceInfo &info = infos.editValueAt(index);
for (size_t i = 0; i < resources.size(); ++i) {
- const auto resType = std::make_pair(resources[i].mType, resources[i].mSubType);
+ const auto &res = resources[i];
+ const auto resType = std::tuple(res.mType, res.mSubType, res.mId);
// ignore if we don't have it
if (info.resources.find(resType) != info.resources.end()) {
MediaResource &resource = info.resources[resType];
- if (resource.mValue > resources[i].mValue) {
- resource.mValue -= resources[i].mValue;
+ if (resource.mValue > res.mValue) {
+ resource.mValue -= res.mValue;
} else {
- onLastRemoved(resources[i], info);
+ // drm sessions always take this branch because res.mValue is set
+ // to UINT64_MAX
+ onLastRemoved(res, info);
info.resources.erase(resType);
}
}
@@ -430,6 +446,7 @@
const MediaResource *secureCodec = NULL;
const MediaResource *nonSecureCodec = NULL;
const MediaResource *graphicMemory = NULL;
+ const MediaResource *drmSession = NULL;
for (size_t i = 0; i < resources.size(); ++i) {
MediaResource::Type type = resources[i].mType;
if (resources[i].mType == MediaResource::kSecureCodec) {
@@ -438,6 +455,8 @@
nonSecureCodec = &resources[i];
} else if (type == MediaResource::kGraphicMemory) {
graphicMemory = &resources[i];
+ } else if (type == MediaResource::kDrmSession) {
+ drmSession = &resources[i];
}
}
@@ -461,6 +480,12 @@
}
}
}
+ if (drmSession != NULL) {
+ getClientForResource_l(callingPid, drmSession, &clients);
+ if (clients.size() == 0) {
+ return false;
+ }
+ }
if (clients.size() == 0) {
// if no secure/non-secure codec conflict, run second pass to handle other resources.
diff --git a/services/mediaresourcemanager/ResourceManagerService.h b/services/mediaresourcemanager/ResourceManagerService.h
index f086dc3..44d0c28 100644
--- a/services/mediaresourcemanager/ResourceManagerService.h
+++ b/services/mediaresourcemanager/ResourceManagerService.h
@@ -33,7 +33,7 @@
class ServiceLog;
struct ProcessInfoInterface;
-typedef std::map<std::pair<MediaResource::Type, MediaResource::SubType>, MediaResource> ResourceList;
+typedef std::map<std::tuple<MediaResource::Type, MediaResource::SubType, std::vector<uint8_t>>, MediaResource> ResourceList;
struct ResourceInfo {
int64_t clientId;
uid_t uid;
@@ -126,6 +126,9 @@
void onFirstAdded(const MediaResource& res, const ResourceInfo& clientInfo);
void onLastRemoved(const MediaResource& res, const ResourceInfo& clientInfo);
+ // Merge r2 into r1
+ void mergeResources(MediaResource& r1, const MediaResource& r2);
+
mutable Mutex mLock;
sp<ProcessInfoInterface> mProcessInfo;
sp<SystemCallbackInterface> mSystemCB;
diff --git a/services/mediaresourcemanager/test/ResourceManagerService_test.cpp b/services/mediaresourcemanager/test/ResourceManagerService_test.cpp
index ae97ec8..9e14151 100644
--- a/services/mediaresourcemanager/test/ResourceManagerService_test.cpp
+++ b/services/mediaresourcemanager/test/ResourceManagerService_test.cpp
@@ -173,8 +173,9 @@
// convert resource1 to ResourceList
ResourceList r1;
for (size_t i = 0; i < resources1.size(); ++i) {
- const auto resType = std::make_pair(resources1[i].mType, resources1[i].mSubType);
- r1[resType] = resources1[i];
+ const auto &res = resources1[i];
+ const auto resType = std::tuple(res.mType, res.mSubType, res.mId);
+ r1[resType] = res;
}
return r1 == resources2;
}