Merge "Make a copy of output format in onOutputFormatChanged"
diff --git a/apex/manifest.json b/apex/manifest.json
index 3011ee8..ddd642e 100644
--- a/apex/manifest.json
+++ b/apex/manifest.json
@@ -1,4 +1,4 @@
{
"name": "com.android.media",
- "version": 290000000
+ "version": 300000000
}
diff --git a/apex/manifest_codec.json b/apex/manifest_codec.json
index 83a5178..2320fd7 100644
--- a/apex/manifest_codec.json
+++ b/apex/manifest_codec.json
@@ -1,4 +1,4 @@
{
"name": "com.android.media.swcodec",
- "version": 290000000
+ "version": 300000000
}
diff --git a/drm/drmserver/Android.bp b/drm/drmserver/Android.bp
index c25a0a1..fd71837 100644
--- a/drm/drmserver/Android.bp
+++ b/drm/drmserver/Android.bp
@@ -25,6 +25,7 @@
shared_libs: [
"libmedia",
+ "libmediametrics",
"libutils",
"liblog",
"libbinder",
diff --git a/drm/drmserver/DrmManager.cpp b/drm/drmserver/DrmManager.cpp
index afbcb39..66610b3 100644
--- a/drm/drmserver/DrmManager.cpp
+++ b/drm/drmserver/DrmManager.cpp
@@ -19,7 +19,10 @@
#include "utils/Log.h"
#include <utils/String8.h>
+
+#include <binder/IPCThreadState.h>
#include <drm/DrmInfo.h>
+
#include <drm/DrmInfoEvent.h>
#include <drm/DrmRights.h>
#include <drm/DrmConstraints.h>
@@ -28,6 +31,7 @@
#include <drm/DrmInfoRequest.h>
#include <drm/DrmSupportInfo.h>
#include <drm/DrmConvertedStatus.h>
+#include <media/MediaAnalyticsItem.h>
#include <IDrmEngine.h>
#include "DrmManager.h"
@@ -50,6 +54,40 @@
}
+void DrmManager::reportEngineMetrics(
+ const char func[], const String8& plugInId, const String8& mimeType) {
+ IDrmEngine& engine = mPlugInManager.getPlugIn(plugInId);
+
+ std::unique_ptr<MediaAnalyticsItem> item(MediaAnalyticsItem::create("drmmanager"));
+ item->generateSessionID();
+ item->setUid(IPCThreadState::self()->getCallingUid());
+ item->setCString("function_name", func);
+ item->setCString("plugin_id", plugInId.getPathLeaf().getBasePath().c_str());
+
+ std::unique_ptr<DrmSupportInfo> info(engine.getSupportInfo(0));
+ if (NULL != info) {
+ item->setCString("description", info->getDescription().c_str());
+ }
+
+ if (!mimeType.isEmpty()) {
+ item->setCString("mime_types", mimeType.c_str());
+ } else if (NULL != info) {
+ DrmSupportInfo::MimeTypeIterator mimeIter = info->getMimeTypeIterator();
+ String8 mimes;
+ while (mimeIter.hasNext()) {
+ mimes += mimeIter.next();
+ if (mimeIter.hasNext()) {
+ mimes += ",";
+ }
+ }
+ item->setCString("mime_types", mimes.c_str());
+ }
+
+ if (!item->selfrecord()) {
+ ALOGE("Failed to record metrics");
+ }
+}
+
int DrmManager::addUniqueId(bool isNative) {
Mutex::Autolock _l(mLock);
@@ -147,27 +185,36 @@
for (size_t index = 0; index < plugInIdList.size(); index++) {
IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInIdList.itemAt(index));
rDrmEngine.terminate(uniqueId);
+ reportEngineMetrics(__func__, plugInIdList[index]);
}
}
DrmConstraints* DrmManager::getConstraints(int uniqueId, const String8* path, const int action) {
Mutex::Autolock _l(mLock);
+ DrmConstraints *constraints = NULL;
const String8 plugInId = getSupportedPlugInIdFromPath(uniqueId, *path);
if (EMPTY_STRING != plugInId) {
IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
- return rDrmEngine.getConstraints(uniqueId, path, action);
+ constraints = rDrmEngine.getConstraints(uniqueId, path, action);
}
- return NULL;
+ if (NULL != constraints) {
+ reportEngineMetrics(__func__, plugInId);
+ }
+ return constraints;
}
DrmMetadata* DrmManager::getMetadata(int uniqueId, const String8* path) {
Mutex::Autolock _l(mLock);
+ DrmMetadata *meta = NULL;
const String8 plugInId = getSupportedPlugInIdFromPath(uniqueId, *path);
if (EMPTY_STRING != plugInId) {
IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
- return rDrmEngine.getMetadata(uniqueId, path);
+ meta = rDrmEngine.getMetadata(uniqueId, path);
}
- return NULL;
+ if (NULL != meta) {
+ reportEngineMetrics(__func__, plugInId);
+ }
+ return meta;
}
bool DrmManager::canHandle(int uniqueId, const String8& path, const String8& mimeType) {
@@ -175,6 +222,10 @@
const String8 plugInId = getSupportedPlugInId(mimeType);
bool result = (EMPTY_STRING != plugInId) ? true : false;
+ if (result) {
+ reportEngineMetrics(__func__, plugInId, mimeType);
+ }
+
if (0 < path.length()) {
if (result) {
IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
@@ -191,12 +242,17 @@
DrmInfoStatus* DrmManager::processDrmInfo(int uniqueId, const DrmInfo* drmInfo) {
Mutex::Autolock _l(mLock);
- const String8 plugInId = getSupportedPlugInId(drmInfo->getMimeType());
+ DrmInfoStatus *infoStatus = NULL;
+ const String8 mimeType = drmInfo->getMimeType();
+ const String8 plugInId = getSupportedPlugInId(mimeType);
if (EMPTY_STRING != plugInId) {
IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
- return rDrmEngine.processDrmInfo(uniqueId, drmInfo);
+ infoStatus = rDrmEngine.processDrmInfo(uniqueId, drmInfo);
}
- return NULL;
+ if (NULL != infoStatus) {
+ reportEngineMetrics(__func__, plugInId, mimeType);
+ }
+ return infoStatus;
}
bool DrmManager::canHandle(int uniqueId, const String8& path) {
@@ -208,6 +264,7 @@
result = rDrmEngine.canHandle(uniqueId, path);
if (result) {
+ reportEngineMetrics(__func__, plugInPathList[i]);
break;
}
}
@@ -216,54 +273,75 @@
DrmInfo* DrmManager::acquireDrmInfo(int uniqueId, const DrmInfoRequest* drmInfoRequest) {
Mutex::Autolock _l(mLock);
- const String8 plugInId = getSupportedPlugInId(drmInfoRequest->getMimeType());
+ DrmInfo *info = NULL;
+ const String8 mimeType = drmInfoRequest->getMimeType();
+ const String8 plugInId = getSupportedPlugInId(mimeType);
if (EMPTY_STRING != plugInId) {
IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
- return rDrmEngine.acquireDrmInfo(uniqueId, drmInfoRequest);
+ info = rDrmEngine.acquireDrmInfo(uniqueId, drmInfoRequest);
}
- return NULL;
+ if (NULL != info) {
+ reportEngineMetrics(__func__, plugInId, mimeType);
+ }
+ return info;
}
status_t DrmManager::saveRights(int uniqueId, const DrmRights& drmRights,
const String8& rightsPath, const String8& contentPath) {
Mutex::Autolock _l(mLock);
- const String8 plugInId = getSupportedPlugInId(drmRights.getMimeType());
+ const String8 mimeType = drmRights.getMimeType();
+ const String8 plugInId = getSupportedPlugInId(mimeType);
status_t result = DRM_ERROR_UNKNOWN;
if (EMPTY_STRING != plugInId) {
IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
result = rDrmEngine.saveRights(uniqueId, drmRights, rightsPath, contentPath);
}
+ if (DRM_NO_ERROR == result) {
+ reportEngineMetrics(__func__, plugInId, mimeType);
+ }
return result;
}
String8 DrmManager::getOriginalMimeType(int uniqueId, const String8& path, int fd) {
Mutex::Autolock _l(mLock);
+ String8 mimeType(EMPTY_STRING);
const String8 plugInId = getSupportedPlugInIdFromPath(uniqueId, path);
if (EMPTY_STRING != plugInId) {
IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
- return rDrmEngine.getOriginalMimeType(uniqueId, path, fd);
+ mimeType = rDrmEngine.getOriginalMimeType(uniqueId, path, fd);
}
- return EMPTY_STRING;
+ if (!mimeType.isEmpty()) {
+ reportEngineMetrics(__func__, plugInId, mimeType);
+ }
+ return mimeType;
}
int DrmManager::getDrmObjectType(int uniqueId, const String8& path, const String8& mimeType) {
Mutex::Autolock _l(mLock);
+ int type = DrmObjectType::UNKNOWN;
const String8 plugInId = getSupportedPlugInId(uniqueId, path, mimeType);
if (EMPTY_STRING != plugInId) {
IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
- return rDrmEngine.getDrmObjectType(uniqueId, path, mimeType);
+ type = rDrmEngine.getDrmObjectType(uniqueId, path, mimeType);
}
- return DrmObjectType::UNKNOWN;
+ if (DrmObjectType::UNKNOWN != type) {
+ reportEngineMetrics(__func__, plugInId, mimeType);
+ }
+ return type;
}
int DrmManager::checkRightsStatus(int uniqueId, const String8& path, int action) {
Mutex::Autolock _l(mLock);
+ int rightsStatus = RightsStatus::RIGHTS_INVALID;
const String8 plugInId = getSupportedPlugInIdFromPath(uniqueId, path);
if (EMPTY_STRING != plugInId) {
IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
- return rDrmEngine.checkRightsStatus(uniqueId, path, action);
+ rightsStatus = rDrmEngine.checkRightsStatus(uniqueId, path, action);
}
- return RightsStatus::RIGHTS_INVALID;
+ if (RightsStatus::RIGHTS_INVALID != rightsStatus) {
+ reportEngineMetrics(__func__, plugInId);
+ }
+ return rightsStatus;
}
status_t DrmManager::consumeRights(
@@ -307,6 +385,9 @@
IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
result = rDrmEngine.removeRights(uniqueId, path);
}
+ if (DRM_NO_ERROR == result) {
+ reportEngineMetrics(__func__, plugInId);
+ }
return result;
}
@@ -319,6 +400,7 @@
if (DRM_NO_ERROR != result) {
break;
}
+ reportEngineMetrics(__func__, plugInIdList[index]);
}
return result;
}
@@ -335,6 +417,7 @@
++mConvertId;
convertId = mConvertId;
mConvertSessionMap.add(convertId, &rDrmEngine);
+ reportEngineMetrics(__func__, plugInId, mimeType);
}
}
return convertId;
@@ -415,6 +498,7 @@
if (DRM_NO_ERROR == result) {
++mDecryptSessionId;
mDecryptSessionMap.add(mDecryptSessionId, &rDrmEngine);
+ reportEngineMetrics(__func__, plugInId, String8(mime));
break;
}
}
@@ -443,6 +527,7 @@
if (DRM_NO_ERROR == result) {
++mDecryptSessionId;
mDecryptSessionMap.add(mDecryptSessionId, &rDrmEngine);
+ reportEngineMetrics(__func__, plugInId, String8(mime));
break;
}
}
@@ -472,6 +557,7 @@
if (DRM_NO_ERROR == result) {
++mDecryptSessionId;
mDecryptSessionMap.add(mDecryptSessionId, &rDrmEngine);
+ reportEngineMetrics(__func__, plugInId, mimeType);
break;
}
}
diff --git a/drm/drmserver/DrmManager.h b/drm/drmserver/DrmManager.h
index 26222bc..75fc1a8 100644
--- a/drm/drmserver/DrmManager.h
+++ b/drm/drmserver/DrmManager.h
@@ -143,6 +143,9 @@
bool canHandle(int uniqueId, const String8& path);
+ void reportEngineMetrics(const char func[],
+ const String8& plugInId, const String8& mimeType = String8(""));
+
private:
enum {
kMaxNumUniqueIds = 0x1000,
diff --git a/media/codec2/components/avc/C2SoftAvcDec.cpp b/media/codec2/components/avc/C2SoftAvcDec.cpp
index 3f015b4..fa98178 100644
--- a/media/codec2/components/avc/C2SoftAvcDec.cpp
+++ b/media/codec2/components/avc/C2SoftAvcDec.cpp
@@ -33,7 +33,8 @@
namespace {
constexpr char COMPONENT_NAME[] = "c2.android.avc.decoder";
-
+constexpr uint32_t kDefaultOutputDelay = 8;
+constexpr uint32_t kMaxOutputDelay = 16;
} // namespace
class C2SoftAvcDec::IntfImpl : public SimpleInterface<void>::BaseParams {
@@ -54,7 +55,9 @@
// TODO: Proper support for reorder depth.
addParameter(
DefineParam(mActualOutputDelay, C2_PARAMKEY_OUTPUT_DELAY)
- .withConstValue(new C2PortActualDelayTuning::output(8u))
+ .withDefault(new C2PortActualDelayTuning::output(kDefaultOutputDelay))
+ .withFields({C2F(mActualOutputDelay, value).inRange(0, kMaxOutputDelay)})
+ .withSetter(Setter<decltype(*mActualOutputDelay)>::StrictValueWithNoDeps)
.build());
// TODO: output latency and reordering
@@ -196,7 +199,6 @@
0u, HAL_PIXEL_FORMAT_YCBCR_420_888))
.build());
}
-
static C2R SizeSetter(bool mayBlock, const C2P<C2StreamPictureSizeInfo::output> &oldMe,
C2P<C2StreamPictureSizeInfo::output> &me) {
(void)mayBlock;
@@ -333,6 +335,7 @@
mDecHandle(nullptr),
mOutBufferFlush(nullptr),
mIvColorFormat(IV_YUV_420P),
+ mOutputDelay(kDefaultOutputDelay),
mWidth(320),
mHeight(240),
mHeaderDecoded(false),
@@ -882,6 +885,26 @@
work->result = C2_CORRUPTED;
return;
}
+ if (s_decode_op.i4_reorder_depth >= 0 && mOutputDelay != s_decode_op.i4_reorder_depth) {
+ mOutputDelay = s_decode_op.i4_reorder_depth;
+ ALOGV("New Output delay %d ", mOutputDelay);
+
+ C2PortActualDelayTuning::output outputDelay(mOutputDelay);
+ std::vector<std::unique_ptr<C2SettingResult>> failures;
+ c2_status_t err =
+ mIntf->config({&outputDelay}, C2_MAY_BLOCK, &failures);
+ if (err == OK) {
+ work->worklets.front()->output.configUpdate.push_back(
+ C2Param::Copy(outputDelay));
+ } else {
+ ALOGE("Cannot set output delay");
+ mSignalledError = true;
+ work->workletsProcessed = 1u;
+ work->result = C2_CORRUPTED;
+ return;
+ }
+ continue;
+ }
if (0 < s_decode_op.u4_pic_wd && 0 < s_decode_op.u4_pic_ht) {
if (mHeaderDecoded == false) {
mHeaderDecoded = true;
diff --git a/media/codec2/components/avc/C2SoftAvcDec.h b/media/codec2/components/avc/C2SoftAvcDec.h
index 72ee583..4414a26 100644
--- a/media/codec2/components/avc/C2SoftAvcDec.h
+++ b/media/codec2/components/avc/C2SoftAvcDec.h
@@ -157,7 +157,7 @@
size_t mNumCores;
IV_COLOR_FORMAT_T mIvColorFormat;
-
+ uint32_t mOutputDelay;
uint32_t mWidth;
uint32_t mHeight;
uint32_t mStride;
diff --git a/media/codec2/components/hevc/C2SoftHevcDec.cpp b/media/codec2/components/hevc/C2SoftHevcDec.cpp
index 7232572..df677c2 100644
--- a/media/codec2/components/hevc/C2SoftHevcDec.cpp
+++ b/media/codec2/components/hevc/C2SoftHevcDec.cpp
@@ -33,7 +33,8 @@
namespace {
constexpr char COMPONENT_NAME[] = "c2.android.hevc.decoder";
-
+constexpr uint32_t kDefaultOutputDelay = 8;
+constexpr uint32_t kMaxOutputDelay = 16;
} // namespace
class C2SoftHevcDec::IntfImpl : public SimpleInterface<void>::BaseParams {
@@ -54,7 +55,9 @@
// TODO: Proper support for reorder depth.
addParameter(
DefineParam(mActualOutputDelay, C2_PARAMKEY_OUTPUT_DELAY)
- .withConstValue(new C2PortActualDelayTuning::output(8u))
+ .withDefault(new C2PortActualDelayTuning::output(kDefaultOutputDelay))
+ .withFields({C2F(mActualOutputDelay, value).inRange(0, kMaxOutputDelay)})
+ .withSetter(Setter<decltype(*mActualOutputDelay)>::StrictValueWithNoDeps)
.build());
addParameter(
@@ -327,6 +330,7 @@
mDecHandle(nullptr),
mOutBufferFlush(nullptr),
mIvColorformat(IV_YUV_420P),
+ mOutputDelay(kDefaultOutputDelay),
mWidth(320),
mHeight(240),
mHeaderDecoded(false),
@@ -877,6 +881,26 @@
work->result = C2_CORRUPTED;
return;
}
+ if (s_decode_op.i4_reorder_depth >= 0 && mOutputDelay != s_decode_op.i4_reorder_depth) {
+ mOutputDelay = s_decode_op.i4_reorder_depth;
+ ALOGV("New Output delay %d ", mOutputDelay);
+
+ C2PortActualDelayTuning::output outputDelay(mOutputDelay);
+ std::vector<std::unique_ptr<C2SettingResult>> failures;
+ c2_status_t err =
+ mIntf->config({&outputDelay}, C2_MAY_BLOCK, &failures);
+ if (err == OK) {
+ work->worklets.front()->output.configUpdate.push_back(
+ C2Param::Copy(outputDelay));
+ } else {
+ ALOGE("Cannot set output delay");
+ mSignalledError = true;
+ work->workletsProcessed = 1u;
+ work->result = C2_CORRUPTED;
+ return;
+ }
+ continue;
+ }
if (0 < s_decode_op.u4_pic_wd && 0 < s_decode_op.u4_pic_ht) {
if (mHeaderDecoded == false) {
mHeaderDecoded = true;
diff --git a/media/codec2/components/hevc/C2SoftHevcDec.h b/media/codec2/components/hevc/C2SoftHevcDec.h
index b7664e6..ce63a6c 100644
--- a/media/codec2/components/hevc/C2SoftHevcDec.h
+++ b/media/codec2/components/hevc/C2SoftHevcDec.h
@@ -115,7 +115,7 @@
size_t mNumCores;
IV_COLOR_FORMAT_T mIvColorformat;
-
+ uint32_t mOutputDelay;
uint32_t mWidth;
uint32_t mHeight;
uint32_t mStride;
diff --git a/media/codec2/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioDecTest.cpp b/media/codec2/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioDecTest.cpp
index 6469735..a8a552c 100644
--- a/media/codec2/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioDecTest.cpp
+++ b/media/codec2/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioDecTest.cpp
@@ -547,6 +547,10 @@
if (mCompName == raw) {
bitStreamInfo[0] = 8000;
bitStreamInfo[1] = 1;
+ } else if (mCompName == g711alaw || mCompName == g711mlaw) {
+ // g711 test data is all 1-channel and has no embedded config info.
+ bitStreamInfo[0] = 8000;
+ bitStreamInfo[1] = 1;
} else {
ASSERT_NO_FATAL_FAILURE(
getInputChannelInfo(mComponent, mCompName, bitStreamInfo));
diff --git a/media/libmedia/Android.bp b/media/libmedia/Android.bp
index 8b97fbe..8de6381 100644
--- a/media/libmedia/Android.bp
+++ b/media/libmedia/Android.bp
@@ -1,10 +1,3 @@
-cc_defaults {
- name: "libmedia_defaults",
- include_dirs: [
- "bionic/libc/private",
- ],
-}
-
cc_library_headers {
name: "libmedia_headers",
vendor_available: true,
@@ -227,8 +220,6 @@
cc_library {
name: "libmedia",
- defaults: [ "libmedia_defaults" ],
-
srcs: [
"IDataSource.cpp",
"BufferingSettings.cpp",
@@ -274,6 +265,7 @@
},
header_libs: [
+ "bionic_libc_platform_headers",
"libstagefright_headers",
"media_ndk_headers",
],
@@ -339,8 +331,6 @@
cc_library_static {
name: "libmedia_player2_util",
- defaults: [ "libmedia_defaults" ],
-
srcs: [
"AudioParameter.cpp",
"BufferingSettings.cpp",
diff --git a/media/libmedia/MediaUtils.cpp b/media/libmedia/MediaUtils.cpp
index 31972fa..2efb30e 100644
--- a/media/libmedia/MediaUtils.cpp
+++ b/media/libmedia/MediaUtils.cpp
@@ -22,7 +22,7 @@
#include <sys/resource.h>
#include <unistd.h>
-#include <bionic_malloc.h>
+#include <bionic/malloc.h>
#include "MediaUtils.h"
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
index 39be40d..c30f048 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -33,6 +33,7 @@
#include <media/stagefright/Utils.h>
#include <media/stagefright/VideoFrameScheduler.h>
#include <media/MediaCodecBuffer.h>
+#include <utils/SystemClock.h>
#include <inttypes.h>
@@ -156,6 +157,7 @@
CHECK(mediaClock != NULL);
mPlaybackRate = mPlaybackSettings.mSpeed;
mMediaClock->setPlaybackRate(mPlaybackRate);
+ (void)mSyncFlag.test_and_set();
}
NuPlayer::Renderer::~Renderer() {
@@ -326,9 +328,27 @@
mSyncQueues = false;
}
+ // Wait until the current job in the message queue is done, to make sure
+ // buffer processing from the old generation is finished. After the current
+ // job is finished, access to buffers are protected by generation.
+ Mutex::Autolock syncLock(mSyncLock);
+ int64_t syncCount = mSyncCount;
+ mSyncFlag.clear();
+
+ // Make sure message queue is not empty after mSyncFlag is cleared.
sp<AMessage> msg = new AMessage(kWhatFlush, this);
msg->setInt32("audio", static_cast<int32_t>(audio));
msg->post();
+
+ int64_t uptimeMs = uptimeMillis();
+ while (mSyncCount == syncCount) {
+ (void)mSyncCondition.waitRelative(mSyncLock, ms2ns(1000));
+ if (uptimeMillis() - uptimeMs > 1000) {
+ ALOGW("flush(): no wake-up from sync point for 1s; stop waiting to "
+ "prevent being stuck indefinitely.");
+ break;
+ }
+ }
}
void NuPlayer::Renderer::signalTimeDiscontinuity() {
@@ -781,6 +801,11 @@
TRESPASS();
break;
}
+ if (!mSyncFlag.test_and_set()) {
+ Mutex::Autolock syncLock(mSyncLock);
+ ++mSyncCount;
+ mSyncCondition.broadcast();
+ }
}
void NuPlayer::Renderer::postDrainAudioQueue_l(int64_t delayUs) {
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h
index a521f62..3d2b033 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h
@@ -18,6 +18,8 @@
#define NUPLAYER_RENDERER_H_
+#include <atomic>
+
#include <media/AudioResamplerPublic.h>
#include <media/AVSyncSettings.h>
@@ -220,6 +222,11 @@
sp<AWakeLock> mWakeLock;
+ std::atomic_flag mSyncFlag = ATOMIC_FLAG_INIT;
+ Mutex mSyncLock;
+ Condition mSyncCondition;
+ int64_t mSyncCount{0};
+
status_t getCurrentPositionOnLooper(int64_t *mediaUs);
status_t getCurrentPositionOnLooper(
int64_t *mediaUs, int64_t nowUs, bool allowPastQueuedVideo = false);
diff --git a/media/libstagefright/bqhelper/GraphicBufferSource.cpp b/media/libstagefright/bqhelper/GraphicBufferSource.cpp
index 59317e7..591b9e9 100644
--- a/media/libstagefright/bqhelper/GraphicBufferSource.cpp
+++ b/media/libstagefright/bqhelper/GraphicBufferSource.cpp
@@ -29,6 +29,7 @@
#include <media/stagefright/foundation/ColorUtils.h>
#include <media/stagefright/foundation/FileDescriptor.h>
+#include <android-base/properties.h>
#include <media/hardware/MetadataBufferType.h>
#include <ui/GraphicBuffer.h>
#include <gui/BufferItem.h>
@@ -800,6 +801,9 @@
}
}
+#ifdef __clang__
+__attribute__((no_sanitize("integer")))
+#endif
bool GraphicBufferSource::calculateCodecTimestamp_l(
nsecs_t bufferTimeNs, int64_t *codecTimeUs) {
int64_t timeUs = bufferTimeNs / 1000;
@@ -816,14 +820,15 @@
mPrevFrameUs = mBaseFrameUs =
std::llround((timeUs * mCaptureFps) / mFps);
mFrameCount = 0;
- } else {
- // snap to nearest capture point
+ } else if (mSnapTimestamps) {
double nFrames = (timeUs - mPrevCaptureUs) * mCaptureFps / 1000000;
if (nFrames < 0.5 - kTimestampFluctuation) {
// skip this frame as it's too close to previous capture
- ALOGD("skipping frame, timeUs %lld", static_cast<long long>(timeUs));
+ ALOGD("skipping frame, timeUs %lld",
+ static_cast<long long>(timeUs));
return false;
}
+ // snap to nearest capture point
if (nFrames <= 1.0) {
nFrames = 1.0;
}
@@ -832,6 +837,22 @@
mFrameCount * 1000000 / mCaptureFps);
mPrevFrameUs = mBaseFrameUs + std::llround(
mFrameCount * 1000000 / mFps);
+ } else {
+ if (timeUs <= mPrevCaptureUs) {
+ if (mFrameDropper != NULL && mFrameDropper->disabled()) {
+ // Warn only, client has disabled frame drop logic possibly for image
+ // encoding cases where camera's ZSL mode could send out of order frames.
+ ALOGW("Received frame that's going backward in time");
+ } else {
+ // Drop the frame if it's going backward in time. Bad timestamp
+ // could disrupt encoder's rate control completely.
+ ALOGW("Dropping frame that's going backward in time");
+ return false;
+ }
+ }
+ mPrevCaptureUs = timeUs;
+ mPrevFrameUs = mBaseFrameUs + std::llround(
+ (timeUs - mBaseCaptureUs) * (mCaptureFps / mFps));
}
ALOGV("timeUs %lld, captureUs %lld, frameUs %lld",
@@ -1359,6 +1380,12 @@
mFps = fps;
mCaptureFps = captureFps;
+ if (captureFps > fps) {
+ mSnapTimestamps = 1 == base::GetIntProperty(
+ "debug.stagefright.snap_timestamps", int64_t(1));
+ } else {
+ mSnapTimestamps = false;
+ }
return OK;
}
diff --git a/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/GraphicBufferSource.h b/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/GraphicBufferSource.h
index b4c4d4a..ed5d7cb 100644
--- a/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/GraphicBufferSource.h
+++ b/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/GraphicBufferSource.h
@@ -461,12 +461,33 @@
// Slow motion mode is enabled if both encoding and capture frame rates are
// defined and the encoding frame rate is less than half the capture frame
// rate. In this mode, the source is expected to produce frames with an even
- // timestamp interval (after rounding) with the configured capture fps. The
- // first source timestamp is used as the source base time. Afterwards, the
- // timestamp of each source frame is snapped to the nearest expected capture
- // timestamp and scaled to match the configured encoding frame rate.
+ // timestamp interval (after rounding) with the configured capture fps.
+ //
+ // These modes must be configured by calling setTimeLapseConfig() before
+ // using this source.
+ //
+ // Timestamp snapping for slow motion recording
+ // ============================================
+ //
+ // When the slow motion mode is configured with setTimeLapseConfig(), the
+ // property "debug.stagefright.snap_timestamps" will be checked. If the
+ // value of the property is set to any value other than 1, mSnapTimestamps
+ // will be set to false. Otherwise, mSnapTimestamps will be set to true.
+ // (mSnapTimestamps will be false for time lapse recording regardless of the
+ // value of the property.)
+ //
+ // If mSnapTimestamps is true, i.e., timestamp snapping is enabled, the
+ // first source timestamp will be used as the source base time; afterwards,
+ // the timestamp of each source frame will be snapped to the nearest
+ // expected capture timestamp and scaled to match the configured encoding
+ // frame rate.
+ //
+ // If timestamp snapping is disabled, the timestamp of source frames will
+ // be scaled to match the ratio between the configured encoding frame rate
+ // and the configured capture frame rate.
- // These modes must be enabled before using this source.
+ // whether timestamps will be snapped
+ bool mSnapTimestamps{true};
// adjusted capture timestamp of the base frame
int64_t mBaseCaptureUs;
diff --git a/media/tests/benchmark/README.md b/media/tests/benchmark/README.md
index 327f0e2..0acfab8 100644
--- a/media/tests/benchmark/README.md
+++ b/media/tests/benchmark/README.md
@@ -28,3 +28,23 @@
```
adb shell /data/local/tmp/extractorTest -P /sdcard/res/
```
+
+## Decoder
+
+The test decodes input stream and benchmarks the decoders available in NDK.
+
+Setup steps are same as extractor.
+
+```
+adb shell /data/local/tmp/decoderTest -P /sdcard/res/
+```
+
+## Muxer
+
+The test muxes elementary stream and benchmarks the muxers available in NDK.
+
+Setup steps are same as extractor.
+
+```
+adb shell /data/local/tmp/muxerTest -P /sdcard/res/
+```
diff --git a/media/tests/benchmark/src/native/common/Android.bp b/media/tests/benchmark/src/native/common/Android.bp
index e4f9ab6..64ea1ee 100644
--- a/media/tests/benchmark/src/native/common/Android.bp
+++ b/media/tests/benchmark/src/native/common/Android.bp
@@ -22,6 +22,7 @@
],
srcs: [
+ "BenchmarkCommon.cpp",
"Timer.cpp",
],
diff --git a/media/tests/benchmark/src/native/common/BenchmarkCommon.cpp b/media/tests/benchmark/src/native/common/BenchmarkCommon.cpp
new file mode 100644
index 0000000..bbc8790
--- /dev/null
+++ b/media/tests/benchmark/src/native/common/BenchmarkCommon.cpp
@@ -0,0 +1,102 @@
+/*
+ * 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 "BenchmarkCommon"
+
+#include "BenchmarkCommon.h"
+#include <iostream>
+
+void CallBackHandle::ioThread() {
+ ALOGV("In %s mIsDone : %d, mSawError : %d ", __func__, mIsDone, mSawError);
+ while (!mIsDone && !mSawError) {
+ auto task = mIOQueue.pop();
+ task();
+ }
+}
+
+void OnInputAvailableCB(AMediaCodec *codec, void *userdata, int32_t index) {
+ ALOGV("OnInputAvailableCB: index(%d)", index);
+ CallBackHandle *self = (CallBackHandle *)userdata;
+ self->getTimer()->addInputTime();
+ self->mIOQueue.push([self, codec, index]() { self->onInputAvailable(codec, index); });
+}
+
+void OnOutputAvailableCB(AMediaCodec *codec, void *userdata, int32_t index,
+ AMediaCodecBufferInfo *bufferInfo) {
+ ALOGV("OnOutputAvailableCB: index(%d), (%d, %d, %lld, 0x%x)", index, bufferInfo->offset,
+ bufferInfo->size, (long long)bufferInfo->presentationTimeUs, bufferInfo->flags);
+ CallBackHandle *self = (CallBackHandle *)userdata;
+ self->getTimer()->addOutputTime();
+ AMediaCodecBufferInfo bufferInfoCopy = *bufferInfo;
+ self->mIOQueue.push([self, codec, index, bufferInfoCopy]() {
+ AMediaCodecBufferInfo bc = bufferInfoCopy;
+ self->onOutputAvailable(codec, index, &bc);
+ });
+}
+
+void OnFormatChangedCB(AMediaCodec *codec, void *userdata, AMediaFormat *format) {
+ ALOGV("OnFormatChangedCB: format(%s)", AMediaFormat_toString(format));
+ CallBackHandle *self = (CallBackHandle *)userdata;
+ self->mIOQueue.push([self, codec, format]() { self->onFormatChanged(codec, format); });
+}
+
+void OnErrorCB(AMediaCodec *codec, void *userdata, media_status_t err, int32_t actionCode,
+ const char *detail) {
+ (void)codec;
+ ALOGV("OnErrorCB: err(%d), actionCode(%d), detail(%s)", err, actionCode, detail);
+ CallBackHandle *self = (CallBackHandle *)userdata;
+ self->mSawError = true;
+}
+
+AMediaCodec *createMediaCodec(AMediaFormat *format, const char *mime, string codecName,
+ bool isEncoder) {
+ ALOGV("In %s", __func__);
+ if (!mime) {
+ ALOGE("Please specify a mime type to create codec");
+ return nullptr;
+ }
+
+ AMediaCodec *codec;
+ if (!codecName.empty()) {
+ codec = AMediaCodec_createCodecByName(codecName.c_str());
+ if (!codec) {
+ ALOGE("Unable to create codec by name: %s", codecName.c_str());
+ return nullptr;
+ }
+ } else {
+ if (isEncoder) {
+ codec = AMediaCodec_createEncoderByType(mime);
+ } else {
+ codec = AMediaCodec_createDecoderByType(mime);
+ }
+ if (!codec) {
+ ALOGE("Unable to create codec by mime: %s", mime);
+ return nullptr;
+ }
+ }
+
+ /* Configure codec with the given format*/
+ const char *s = AMediaFormat_toString(format);
+ ALOGV("Input format: %s\n", s);
+
+ media_status_t status = AMediaCodec_configure(codec, format, nullptr, nullptr, isEncoder);
+ if (status != AMEDIA_OK) {
+ ALOGE("AMediaCodec_configure failed %d", status);
+ 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 bad6346..7b87c04 100644
--- a/media/tests/benchmark/src/native/common/BenchmarkCommon.h
+++ b/media/tests/benchmark/src/native/common/BenchmarkCommon.h
@@ -19,11 +19,107 @@
#include <utils/Log.h>
+#include <inttypes.h>
+#include <mutex>
+#include <queue>
+#include <thread>
+
#include <media/NdkMediaCodec.h>
#include <media/NdkMediaError.h>
+#include "Timer.h"
+
+using namespace std;
+
constexpr uint32_t kQueueDequeueTimeoutUs = 1000;
constexpr uint32_t kMaxCSDStrlen = 16;
constexpr uint32_t kMaxBufferSize = 1024 * 1024 * 16;
+template <typename T>
+class CallBackQueue {
+ public:
+ CallBackQueue() {}
+ ~CallBackQueue() {}
+
+ void push(T elem) {
+ bool needsNotify = false;
+ {
+ lock_guard<mutex> lock(mMutex);
+ needsNotify = mQueue.empty();
+ mQueue.push(move(elem));
+ }
+ if (needsNotify) mQueueNotEmptyCondition.notify_one();
+ }
+
+ T pop() {
+ unique_lock<mutex> lock(mMutex);
+ if (mQueue.empty()) {
+ mQueueNotEmptyCondition.wait(lock, [this]() { return !mQueue.empty(); });
+ }
+ auto result = mQueue.front();
+ mQueue.pop();
+ return result;
+ }
+
+ private:
+ mutex mMutex;
+ queue<T> mQueue;
+ condition_variable mQueueNotEmptyCondition;
+};
+
+class CallBackHandle {
+ public:
+ CallBackHandle() : mSawError(false), mIsDone(false), mTimer(nullptr) {}
+
+ virtual ~CallBackHandle() {
+ if (mIOThread.joinable()) mIOThread.join();
+ if (mTimer) delete mTimer;
+ }
+
+ void ioThread();
+
+ // Implementation in child class (Decoder/Encoder)
+ virtual void onInputAvailable(AMediaCodec *codec, int32_t index) {
+ (void)codec;
+ (void)index;
+ }
+ virtual void onFormatChanged(AMediaCodec *codec, AMediaFormat *format) {
+ (void)codec;
+ (void)format;
+ }
+ virtual void onOutputAvailable(AMediaCodec *codec, int32_t index,
+ AMediaCodecBufferInfo *bufferInfo) {
+ (void)codec;
+ (void)index;
+ (void)bufferInfo;
+ }
+
+ virtual Timer *getTimer() { return mTimer; }
+
+ // Keep a queue of all function callbacks.
+ typedef function<void()> IOTask;
+ CallBackQueue<IOTask> mIOQueue;
+ thread mIOThread;
+ bool mSawError;
+ bool mIsDone;
+
+ private:
+ Timer *mTimer;
+};
+
+// Async API's callback
+void OnInputAvailableCB(AMediaCodec *codec, void *userdata, int32_t index);
+
+void OnOutputAvailableCB(AMediaCodec *codec, void *userdata, int32_t index,
+ AMediaCodecBufferInfo *bufferInfo);
+
+void OnFormatChangedCB(AMediaCodec *codec, void *userdata, AMediaFormat *format);
+
+void OnErrorCB(AMediaCodec *codec, void * /* userdata */, media_status_t err, int32_t actionCode,
+ const char *detail);
+
+// Utility to create and configure AMediaCodec
+AMediaCodec *createMediaCodec(AMediaFormat *format, const char *mime, string codecName,
+ bool isEncoder);
+
#endif // __BENCHMARK_COMMON_H__
diff --git a/media/tests/benchmark/src/native/decoder/Android.bp b/media/tests/benchmark/src/native/decoder/Android.bp
new file mode 100644
index 0000000..f2d3db5
--- /dev/null
+++ b/media/tests/benchmark/src/native/decoder/Android.bp
@@ -0,0 +1,31 @@
+/*
+ * 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_library_static {
+ name: "libbenchmark_decoder",
+ defaults: [
+ "libbenchmark_common-defaults",
+ "libbenchmark_soft_sanitize_all-defaults",
+ ],
+
+ srcs: ["Decoder.cpp"],
+
+ static_libs: ["libbenchmark_extractor"],
+
+ export_include_dirs: ["."],
+
+ ldflags: ["-Wl,-Bsymbolic"]
+}
diff --git a/media/tests/benchmark/src/native/decoder/Decoder.cpp b/media/tests/benchmark/src/native/decoder/Decoder.cpp
new file mode 100644
index 0000000..21fc420
--- /dev/null
+++ b/media/tests/benchmark/src/native/decoder/Decoder.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 "decoder"
+
+#include <iostream>
+
+#include "Decoder.h"
+
+tuple<ssize_t, uint32_t, int64_t> readSampleData(uint8_t *inputBuffer, int32_t &offset,
+ vector<AMediaCodecBufferInfo> &frameInfo,
+ uint8_t *buf, int32_t frameID, size_t bufSize) {
+ ALOGV("In %s", __func__);
+ if (frameID == (int32_t)frameInfo.size()) {
+ return make_tuple(0, AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM, 0);
+ }
+ uint32_t flags = frameInfo[frameID].flags;
+ int64_t timestamp = frameInfo[frameID].presentationTimeUs;
+ ssize_t bytesCount = frameInfo[frameID].size;
+ if (bufSize < bytesCount) {
+ ALOGE("Error : insufficient resource");
+ return make_tuple(0, AMEDIACODEC_ERROR_INSUFFICIENT_RESOURCE, 0);
+ }
+
+ memcpy(buf, inputBuffer + offset, bytesCount);
+ offset += bytesCount;
+ return make_tuple(bytesCount, flags, timestamp);
+}
+
+void Decoder::onInputAvailable(AMediaCodec *mediaCodec, int32_t bufIdx) {
+ ALOGV("In %s", __func__);
+ if (mediaCodec == mCodec && mediaCodec) {
+ if (mSawInputEOS || bufIdx < 0) return;
+ if (mSignalledError) {
+ CallBackHandle::mSawError = true;
+ mDecoderDoneCondition.notify_one();
+ return;
+ }
+
+ size_t bufSize;
+ uint8_t *buf = AMediaCodec_getInputBuffer(mCodec, bufIdx, &bufSize);
+ if (!buf) {
+ mSignalledError = true;
+ mDecoderDoneCondition.notify_one();
+ return;
+ }
+
+ ssize_t bytesRead = 0;
+ uint32_t flag = 0;
+ int64_t presentationTimeUs = 0;
+ tie(bytesRead, flag, presentationTimeUs) = readSampleData(
+ mInputBuffer, mOffset, mFrameMetaData, buf, mNumInputFrame, bufSize);
+ if (flag == AMEDIACODEC_ERROR_INSUFFICIENT_RESOURCE) {
+ mSignalledError = true;
+ mDecoderDoneCondition.notify_one();
+ return;
+ }
+
+ if (flag == AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) mSawInputEOS = true;
+ ALOGV("%s bytesRead : %zd presentationTimeUs : %" PRId64 " mSawInputEOS : %s", __FUNCTION__,
+ bytesRead, presentationTimeUs, mSawInputEOS ? "TRUE" : "FALSE");
+
+ int status = AMediaCodec_queueInputBuffer(mCodec, bufIdx, 0 /* offset */, bytesRead,
+ presentationTimeUs, flag);
+ if (AMEDIA_OK != status) {
+ mSignalledError = true;
+ mDecoderDoneCondition.notify_one();
+ return;
+ }
+ mNumInputFrame++;
+ }
+}
+
+void Decoder::onOutputAvailable(AMediaCodec *mediaCodec, int32_t bufIdx,
+ AMediaCodecBufferInfo *bufferInfo) {
+ ALOGV("In %s", __func__);
+ if (mediaCodec == mCodec && mediaCodec) {
+ if (mSawOutputEOS || bufIdx < 0) return;
+ if (mSignalledError) {
+ CallBackHandle::mSawError = true;
+ mDecoderDoneCondition.notify_one();
+ return;
+ }
+
+ AMediaCodec_releaseOutputBuffer(mCodec, bufIdx, false);
+ mSawOutputEOS = (0 != (bufferInfo->flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM));
+ mNumOutputFrame++;
+ ALOGV("%s index : %d mSawOutputEOS : %s count : %u", __FUNCTION__, bufIdx,
+ mSawOutputEOS ? "TRUE" : "FALSE", mNumOutputFrame);
+
+ if (mSawOutputEOS) {
+ CallBackHandle::mIsDone = true;
+ mDecoderDoneCondition.notify_one();
+ }
+ }
+}
+
+void Decoder::onFormatChanged(AMediaCodec *mediaCodec, AMediaFormat *format) {
+ ALOGV("In %s", __func__);
+ if (mediaCodec == mCodec && mediaCodec) {
+ ALOGV("%s { %s }", __FUNCTION__, AMediaFormat_toString(format));
+ mFormat = format;
+ }
+}
+
+void Decoder::setupDecoder() {
+ if (!mFormat) mFormat = mExtractor->getFormat();
+ if (!mTimer) mTimer = new Timer();
+}
+
+int32_t Decoder::decode(uint8_t *inputBuffer, vector<AMediaCodecBufferInfo> &frameInfo,
+ string &codecName, bool asyncMode) {
+ ALOGV("In %s", __func__);
+ mInputBuffer = inputBuffer;
+ mFrameMetaData = frameInfo;
+ mOffset = 0;
+
+ const char *mime = nullptr;
+ AMediaFormat_getString(mFormat, AMEDIAFORMAT_KEY_MIME, &mime);
+ if (!mime) return AMEDIA_ERROR_INVALID_OBJECT;
+
+ int64_t sTime = mTimer->getCurTime();
+ mCodec = createMediaCodec(mFormat, mime, codecName, false /*isEncoder*/);
+ if (!mCodec) return AMEDIA_ERROR_INVALID_OBJECT;
+
+ if (asyncMode) {
+ AMediaCodecOnAsyncNotifyCallback aCB = {OnInputAvailableCB, OnOutputAvailableCB,
+ OnFormatChangedCB, OnErrorCB};
+ AMediaCodec_setAsyncNotifyCallback(mCodec, aCB, this);
+
+ CallBackHandle *callbackHandle = new CallBackHandle();
+ callbackHandle->mIOThread = thread(&CallBackHandle::ioThread, this);
+ }
+
+ AMediaCodec_start(mCodec);
+ int64_t eTime = mTimer->getCurTime();
+ int64_t timeTaken = mTimer->getTimeDiff(sTime, eTime);
+ mTimer->setInitTime(timeTaken);
+
+ mTimer->setStartTime();
+ if (!asyncMode) {
+ while (!mSawOutputEOS && !mSignalledError) {
+ /* Queue input data */
+ if (!mSawInputEOS) {
+ ssize_t inIdx = AMediaCodec_dequeueInputBuffer(mCodec, kQueueDequeueTimeoutUs);
+ if (inIdx < 0 && inIdx != AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
+ ALOGE("AMediaCodec_dequeueInputBuffer returned invalid index %zd\n", inIdx);
+ return AMEDIA_ERROR_IO;
+ } else if (inIdx >= 0) {
+ mTimer->addInputTime();
+ onInputAvailable(mCodec, inIdx);
+ }
+ }
+
+ /* Dequeue output data */
+ AMediaCodecBufferInfo info;
+ ssize_t outIdx = AMediaCodec_dequeueOutputBuffer(mCodec, &info, kQueueDequeueTimeoutUs);
+ if (outIdx == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
+ mFormat = AMediaCodec_getOutputFormat(mCodec);
+ const char *s = AMediaFormat_toString(mFormat);
+ ALOGI("Output format: %s\n", s);
+ } else if (outIdx >= 0) {
+ mTimer->addOutputTime();
+ onOutputAvailable(mCodec, outIdx, &info);
+ } else if (!(outIdx == AMEDIACODEC_INFO_TRY_AGAIN_LATER ||
+ outIdx == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED)) {
+ ALOGE("AMediaCodec_dequeueOutputBuffer returned invalid index %zd\n", outIdx);
+ return AMEDIA_ERROR_IO;
+ }
+ }
+ } else {
+ unique_lock<mutex> lock(mMutex);
+ mDecoderDoneCondition.wait(lock, [this]() { return (mSawOutputEOS || mSignalledError); });
+ }
+
+ if (codecName.empty()) {
+ char *decName;
+ AMediaCodec_getName(mCodec, &decName);
+ codecName.assign(decName);
+ AMediaCodec_releaseName(mCodec, decName);
+ }
+ return AMEDIA_OK;
+}
+
+void Decoder::deInitCodec() {
+ int64_t sTime = mTimer->getCurTime();
+ if (mFormat) {
+ AMediaFormat_delete(mFormat);
+ mFormat = nullptr;
+ }
+ if (!mCodec) return;
+ AMediaCodec_stop(mCodec);
+ AMediaCodec_delete(mCodec);
+ int64_t eTime = mTimer->getCurTime();
+ int64_t timeTaken = mTimer->getTimeDiff(sTime, eTime);
+ mTimer->setDeInitTime(timeTaken);
+}
+
+void Decoder::dumpStatistics(string inputReference) {
+ int64_t durationUs = mExtractor->getClipDuration();
+ string operation = "decode";
+ mTimer->dumpStatistics(operation, inputReference, durationUs);
+}
+
+void Decoder::resetDecoder() {
+ if (mTimer) mTimer->resetTimers();
+ if (mInputBuffer) mInputBuffer = nullptr;
+ if (!mFrameMetaData.empty()) mFrameMetaData.clear();
+}
diff --git a/media/tests/benchmark/src/native/decoder/Decoder.h b/media/tests/benchmark/src/native/decoder/Decoder.h
new file mode 100644
index 0000000..0a64128
--- /dev/null
+++ b/media/tests/benchmark/src/native/decoder/Decoder.h
@@ -0,0 +1,105 @@
+/*
+ * 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 __DECODER_H__
+#define __DECODER_H__
+
+#include <chrono>
+#include <condition_variable>
+#include <mutex>
+#include <queue>
+#include <thread>
+
+#include "BenchmarkCommon.h"
+#include "Extractor.h"
+#include "Timer.h"
+
+class Decoder : public CallBackHandle {
+ public:
+ Decoder()
+ : mCodec(nullptr),
+ mFormat(nullptr),
+ mExtractor(nullptr),
+ mTimer(nullptr),
+ mNumInputFrame(0),
+ mNumOutputFrame(0),
+ mSawInputEOS(false),
+ mSawOutputEOS(false),
+ mSignalledError(false),
+ mInputBuffer(nullptr) {
+ mExtractor = new Extractor();
+ }
+
+ virtual ~Decoder() {
+ if (mTimer) delete mTimer;
+ if (mExtractor) delete mExtractor;
+ }
+
+ Timer *getTimer() override { return mTimer; }
+
+ Extractor *getExtractor() { return mExtractor; }
+
+ // Decoder related utilities
+ void setupDecoder();
+
+ void deInitCodec();
+
+ void resetDecoder();
+
+ // Async callback APIs
+ void onInputAvailable(AMediaCodec *codec, int32_t index) override;
+
+ void onFormatChanged(AMediaCodec *codec, AMediaFormat *format) override;
+
+ void onOutputAvailable(AMediaCodec *codec, int32_t index,
+ AMediaCodecBufferInfo *bufferInfo) override;
+
+ // Process the frames and give decoded output
+ int32_t decode(uint8_t *inputBuffer, vector<AMediaCodecBufferInfo> &frameInfo,
+ string &codecName, bool asyncMode);
+
+ void dumpStatistics(string inputReference);
+
+ private:
+ AMediaCodec *mCodec;
+ AMediaFormat *mFormat;
+
+ Extractor *mExtractor;
+
+ Timer *mTimer;
+
+ int32_t mNumInputFrame;
+ int32_t mNumOutputFrame;
+
+ bool mSawInputEOS;
+ bool mSawOutputEOS;
+ bool mSignalledError;
+
+ int32_t mOffset;
+ uint8_t *mInputBuffer;
+ vector<AMediaCodecBufferInfo> mFrameMetaData;
+
+ /* Asynchronous locks */
+ mutex mMutex;
+ condition_variable mDecoderDoneCondition;
+};
+
+// Read input samples
+tuple<ssize_t, uint32_t, int64_t> readSampleData(uint8_t *inputBuffer, int32_t &offset,
+ vector<AMediaCodecBufferInfo> &frameSizes,
+ uint8_t *buf, int32_t frameID, size_t bufSize);
+
+#endif // __DECODER_H__
diff --git a/media/tests/benchmark/src/native/muxer/Android.bp b/media/tests/benchmark/src/native/muxer/Android.bp
new file mode 100644
index 0000000..6ef2a2e
--- /dev/null
+++ b/media/tests/benchmark/src/native/muxer/Android.bp
@@ -0,0 +1,31 @@
+/*
+ * 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_library_static {
+ name: "libbenchmark_muxer",
+ defaults: [
+ "libbenchmark_common-defaults",
+ "libbenchmark_soft_sanitize_all-defaults",
+ ],
+
+ srcs: ["Muxer.cpp"],
+
+ static_libs: ["libbenchmark_extractor"],
+
+ export_include_dirs: ["."],
+
+ ldflags: ["-Wl,-Bsymbolic"]
+}
diff --git a/media/tests/benchmark/src/native/muxer/Muxer.cpp b/media/tests/benchmark/src/native/muxer/Muxer.cpp
new file mode 100644
index 0000000..877f7ad
--- /dev/null
+++ b/media/tests/benchmark/src/native/muxer/Muxer.cpp
@@ -0,0 +1,89 @@
+/*
+ * 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 "muxer"
+
+#include <fstream>
+#include <iostream>
+
+#include "Muxer.h"
+
+int32_t Muxer::initMuxer(int32_t fd, MUXER_OUTPUT_T outputFormat) {
+ if (!mFormat) mFormat = mExtractor->getFormat();
+ if (!mTimer) mTimer = new Timer();
+
+ int64_t sTime = mTimer->getCurTime();
+ mMuxer = AMediaMuxer_new(fd, (OutputFormat)outputFormat);
+ if (!mMuxer) {
+ cout << "[ WARN ] Test Skipped. Unable to create muxer \n";
+ return AMEDIA_ERROR_INVALID_OBJECT;
+ }
+ /*
+ * AMediaMuxer_addTrack returns the index of the new track or a negative value
+ * in case of failure, which can be interpreted as a media_status_t.
+ */
+ ssize_t index = AMediaMuxer_addTrack(mMuxer, mFormat);
+ if (index < 0) {
+ cout << "[ WARN ] Test Skipped. Format not supported \n";
+ return index;
+ }
+ AMediaMuxer_start(mMuxer);
+ int64_t eTime = mTimer->getCurTime();
+ int64_t timeTaken = mTimer->getTimeDiff(sTime, eTime);
+ mTimer->setInitTime(timeTaken);
+ return AMEDIA_OK;
+}
+
+void Muxer::deInitMuxer() {
+ int64_t sTime = mTimer->getCurTime();
+ if (mFormat) {
+ AMediaFormat_delete(mFormat);
+ mFormat = nullptr;
+ }
+ if (!mMuxer) return;
+ AMediaMuxer_stop(mMuxer);
+ AMediaMuxer_delete(mMuxer);
+ int64_t eTime = mTimer->getCurTime();
+ int64_t timeTaken = mTimer->getTimeDiff(sTime, eTime);
+ mTimer->setDeInitTime(timeTaken);
+}
+
+void Muxer::resetMuxer() {
+ if (mTimer) mTimer->resetTimers();
+}
+
+void Muxer::dumpStatistics(string inputReference) {
+ string operation = "mux";
+ mTimer->dumpStatistics(operation, inputReference, mExtractor->getClipDuration());
+}
+
+int32_t Muxer::mux(uint8_t *inputBuffer, vector<AMediaCodecBufferInfo> &frameInfos) {
+ // Mux frame data
+ size_t frameIdx = 0;
+ mTimer->setStartTime();
+ while (frameIdx < frameInfos.size()) {
+ AMediaCodecBufferInfo info = frameInfos.at(frameIdx);
+ media_status_t status = AMediaMuxer_writeSampleData(mMuxer, 0, inputBuffer, &info);
+ if (status != 0) {
+ ALOGE("Error in AMediaMuxer_writeSampleData");
+ return status;
+ }
+ mTimer->addOutputTime();
+ frameIdx++;
+ }
+ return AMEDIA_OK;
+}
diff --git a/media/tests/benchmark/src/native/muxer/Muxer.h b/media/tests/benchmark/src/native/muxer/Muxer.h
new file mode 100644
index 0000000..154fd20
--- /dev/null
+++ b/media/tests/benchmark/src/native/muxer/Muxer.h
@@ -0,0 +1,63 @@
+/*
+ * 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 __MUXER_H__
+#define __MUXER_H__
+
+#include <media/NdkMediaMuxer.h>
+
+#include "BenchmarkCommon.h"
+#include "Timer.h"
+#include "Extractor.h"
+
+typedef enum {
+ MUXER_OUTPUT_FORMAT_MPEG_4 = 0,
+ MUXER_OUTPUT_FORMAT_WEBM = 1,
+ MUXER_OUTPUT_FORMAT_3GPP = 2,
+ MUXER_OUTPUT_FORMAT_OGG = 4,
+ MUXER_OUTPUT_FORMAT_INVALID = 5,
+} MUXER_OUTPUT_T;
+
+class Muxer {
+ public:
+ Muxer() : mFormat(nullptr), mMuxer(nullptr), mTimer(nullptr) { mExtractor = new Extractor(); }
+
+ virtual ~Muxer() {
+ if (mTimer) delete mTimer;
+ if (mExtractor) delete mExtractor;
+ }
+
+ Timer *getTimer() { return mTimer; }
+ Extractor *getExtractor() { return mExtractor; }
+
+ /* Muxer related utilities */
+ int32_t initMuxer(int32_t fd, MUXER_OUTPUT_T outputFormat);
+ void deInitMuxer();
+ void resetMuxer();
+
+ /* Process the frames and give Muxed output */
+ int32_t mux(uint8_t *inputBuffer, vector<AMediaCodecBufferInfo> &frameSizes);
+
+ void dumpStatistics(string inputReference);
+
+ private:
+ AMediaFormat *mFormat;
+ AMediaMuxer *mMuxer;
+ Extractor *mExtractor;
+ Timer *mTimer;
+};
+
+#endif // __MUXER_H__
diff --git a/media/tests/benchmark/tests/Android.bp b/media/tests/benchmark/tests/Android.bp
index 30d6e20..353d60e 100644
--- a/media/tests/benchmark/tests/Android.bp
+++ b/media/tests/benchmark/tests/Android.bp
@@ -26,3 +26,35 @@
static_libs: ["libbenchmark_extractor"]
}
+
+cc_test {
+ name: "decoderTest",
+ gtest: true,
+ defaults: [
+ "libbenchmark_common-defaults",
+ "libbenchmark_soft_sanitize_all-defaults",
+ ],
+
+ srcs: ["DecoderTest.cpp"],
+
+ static_libs: [
+ "libbenchmark_extractor",
+ "libbenchmark_decoder",
+ ],
+}
+
+cc_test {
+ name: "muxerTest",
+ gtest: true,
+ defaults: [
+ "libbenchmark_common-defaults",
+ "libbenchmark_soft_sanitize_all-defaults",
+ ],
+
+ srcs: ["MuxerTest.cpp"],
+
+ static_libs: [
+ "libbenchmark_extractor",
+ "libbenchmark_muxer",
+ ],
+}
diff --git a/media/tests/benchmark/tests/DecoderTest.cpp b/media/tests/benchmark/tests/DecoderTest.cpp
new file mode 100644
index 0000000..6cb42d6
--- /dev/null
+++ b/media/tests/benchmark/tests/DecoderTest.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 "decoderTest"
+
+#include <fstream>
+#include <iostream>
+#include <limits>
+
+#include "Decoder.h"
+#include "BenchmarkTestEnvironment.h"
+
+static BenchmarkTestEnvironment *gEnv = nullptr;
+
+class DecoderTest : public ::testing::TestWithParam<tuple<string, string, bool>> {};
+
+TEST_P(DecoderTest, Decode) {
+ ALOGV("Decode the samples given by extractor");
+ tuple<string /* InputFile */, string /* CodecName */, bool /* asyncMode */> params = GetParam();
+
+ 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;
+ }
+
+ Decoder *decoder = new Decoder();
+ Extractor *extractor = decoder->getExtractor();
+ if (!extractor) {
+ cout << "[ WARN ] Test Skipped. Extractor creation failed \n";
+ return;
+ }
+
+ // Read file properties
+ fseek(inputFp, 0, SEEK_END);
+ size_t fileSize = ftell(inputFp);
+ fseek(inputFp, 0, SEEK_SET);
+ int32_t fd = fileno(inputFp);
+
+ int32_t trackCount = extractor->initExtractor(fd, fileSize);
+ if (trackCount <= 0) {
+ cout << "[ WARN ] Test Skipped. initExtractor failed\n";
+ return;
+ }
+ 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;
+ }
+
+ uint8_t *inputBuffer = (uint8_t *)malloc(kMaxBufferSize);
+ if (!inputBuffer) {
+ cout << "[ WARN ] Test Skipped. Insufficient memory \n";
+ return;
+ }
+ vector<AMediaCodecBufferInfo> frameInfo;
+ AMediaCodecBufferInfo info;
+ uint32_t inputBufferOffset = 0;
+ int32_t idx = 0;
+
+ // Get CSD data
+ while (1) {
+ 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 > kMaxBufferSize) {
+ cout << "[ WARN ] Test Skipped. Memory allocated not sufficient\n";
+ free(inputBuffer);
+ return;
+ }
+ memcpy(inputBuffer + inputBufferOffset, csdBuffer, info.size);
+ frameInfo.push_back(info);
+ inputBufferOffset += info.size;
+ idx++;
+ }
+ // 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) {
+ cout << "[ WARN ] Test Skipped. Memory allocated not sufficient\n";
+ free(inputBuffer);
+ return;
+ }
+ memcpy(inputBuffer + inputBufferOffset, extractor->getFrameBuf(), info.size);
+ frameInfo.push_back(info);
+ inputBufferOffset += info.size;
+ }
+
+ string codecName = get<1>(params);
+ bool asyncMode = get<2>(params);
+ decoder->setupDecoder();
+ status = decoder->decode(inputBuffer, frameInfo, codecName, asyncMode);
+ if (status != AMEDIA_OK) {
+ cout << "[ WARN ] Test Skipped. Decode returned error \n";
+ free(inputBuffer);
+ return;
+ }
+ decoder->deInitCodec();
+ cout << "codec : " << codecName << endl;
+ string inputReference = get<0>(params);
+ decoder->dumpStatistics(inputReference);
+ free(inputBuffer);
+ decoder->resetDecoder();
+ }
+ fclose(inputFp);
+ extractor->deInitExtractor();
+ delete decoder;
+}
+
+// TODO: (b/140549596)
+// Add wav files
+INSTANTIATE_TEST_SUITE_P(
+ AudioDecoderSyncTest, DecoderTest,
+ ::testing::Values(make_tuple("bbb_44100hz_2ch_128kbps_aac_30sec.mp4", "", false),
+ make_tuple("bbb_44100hz_2ch_128kbps_mp3_30sec.mp3", "", false),
+ make_tuple("bbb_8000hz_1ch_8kbps_amrnb_30sec.3gp", "", false),
+ make_tuple("bbb_16000hz_1ch_9kbps_amrwb_30sec.3gp", "", false),
+ make_tuple("bbb_44100hz_2ch_80kbps_vorbis_30sec.mp4", "", false),
+ make_tuple("bbb_48000hz_2ch_100kbps_opus_30sec.webm", "", false)));
+
+INSTANTIATE_TEST_SUITE_P(
+ AudioDecoderAsyncTest, DecoderTest,
+ ::testing::Values(make_tuple("bbb_44100hz_2ch_128kbps_aac_30sec.mp4", "", true),
+ make_tuple("bbb_44100hz_2ch_128kbps_mp3_30sec.mp3", "", true),
+ make_tuple("bbb_8000hz_1ch_8kbps_amrnb_30sec.3gp", "", true),
+ make_tuple("bbb_16000hz_1ch_9kbps_amrwb_30sec.3gp", "", true),
+ make_tuple("bbb_44100hz_2ch_80kbps_vorbis_30sec.mp4", "", true),
+ make_tuple("bbb_48000hz_2ch_100kbps_opus_30sec.webm", "", true)));
+
+INSTANTIATE_TEST_SUITE_P(VideDecoderSyncTest, DecoderTest,
+ ::testing::Values(
+ // Hardware codecs
+ make_tuple("crowd_1920x1080_25fps_4000kbps_vp9.webm", "", false),
+ make_tuple("crowd_1920x1080_25fps_4000kbps_vp8.webm", "", false),
+ make_tuple("crowd_1920x1080_25fps_4000kbps_av1.webm", "", false),
+ make_tuple("crowd_1920x1080_25fps_7300kbps_mpeg2.mp4", "", false),
+ make_tuple("crowd_1920x1080_25fps_6000kbps_mpeg4.mp4", "", false),
+ make_tuple("crowd_352x288_25fps_6000kbps_h263.3gp", "", false),
+ make_tuple("crowd_1920x1080_25fps_6700kbps_h264.ts", "", false),
+ make_tuple("crowd_1920x1080_25fps_4000kbps_h265.mkv", "", false),
+ // Software codecs
+ make_tuple("crowd_1920x1080_25fps_4000kbps_vp9.webm",
+ "c2.android.vp9.decoder", false),
+ make_tuple("crowd_1920x1080_25fps_4000kbps_vp8.webm",
+ "c2.android.vp8.decoder", false),
+ make_tuple("crowd_1920x1080_25fps_4000kbps_av1.webm",
+ "c2.android.av1.decoder", false),
+ make_tuple("crowd_1920x1080_25fps_7300kbps_mpeg2.mp4",
+ "c2.android.mpeg2.decoder", false),
+ make_tuple("crowd_1920x1080_25fps_6000kbps_mpeg4.mp4",
+ "c2.android.mpeg4.decoder", false),
+ make_tuple("crowd_352x288_25fps_6000kbps_h263.3gp",
+ "c2.android.h263.decoder", false),
+ make_tuple("crowd_1920x1080_25fps_6700kbps_h264.ts",
+ "c2.android.avc.decoder", false),
+ make_tuple("crowd_1920x1080_25fps_4000kbps_h265.mkv",
+ "c2.android.hevc.decoder", false)));
+
+INSTANTIATE_TEST_SUITE_P(VideoDecoderAsyncTest, DecoderTest,
+ ::testing::Values(
+ // Hardware codecs
+ make_tuple("crowd_1920x1080_25fps_4000kbps_vp9.webm", "", true),
+ make_tuple("crowd_1920x1080_25fps_4000kbps_vp8.webm", "", true),
+ make_tuple("crowd_1920x1080_25fps_4000kbps_av1.webm", "", true),
+ make_tuple("crowd_1920x1080_25fps_7300kbps_mpeg2.mp4", "", true),
+ make_tuple("crowd_1920x1080_25fps_6000kbps_mpeg4.mp4", "", true),
+ make_tuple("crowd_352x288_25fps_6000kbps_h263.3gp", "", true),
+ make_tuple("crowd_1920x1080_25fps_6700kbps_h264.ts", "", true),
+ make_tuple("crowd_1920x1080_25fps_4000kbps_h265.mkv", "", true),
+ // Software codecs
+ make_tuple("crowd_1920x1080_25fps_4000kbps_vp9.webm",
+ "c2.android.vp9.decoder", true),
+ make_tuple("crowd_1920x1080_25fps_4000kbps_vp8.webm",
+ "c2.android.vp8.decoder", true),
+ make_tuple("crowd_1920x1080_25fps_4000kbps_av1.webm",
+ "c2.android.av1.decoder", true),
+ make_tuple("crowd_1920x1080_25fps_7300kbps_mpeg2.mp4",
+ "c2.android.mpeg2.decoder", true),
+ make_tuple("crowd_1920x1080_25fps_6000kbps_mpeg4.mp4",
+ "c2.android.mpeg4.decoder", true),
+ make_tuple("crowd_352x288_25fps_6000kbps_h263.3gp",
+ "c2.android.h263.decoder", true),
+ make_tuple("crowd_1920x1080_25fps_6700kbps_h264.ts",
+ "c2.android.avc.decoder", true),
+ make_tuple("crowd_1920x1080_25fps_4000kbps_h265.mkv",
+ "c2.android.hevc.decoder", true)));
+
+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();
+ ALOGD("Decoder Test result = %d\n", status);
+ }
+ return status;
+}
\ No newline at end of file
diff --git a/media/tests/benchmark/tests/MuxerTest.cpp b/media/tests/benchmark/tests/MuxerTest.cpp
new file mode 100644
index 0000000..e814f90
--- /dev/null
+++ b/media/tests/benchmark/tests/MuxerTest.cpp
@@ -0,0 +1,181 @@
+
+/*
+ * 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 "muxerTest"
+
+#include <fstream>
+#include <iostream>
+
+#include "Muxer.h"
+#include "BenchmarkTestEnvironment.h"
+
+#define OUTPUT_FILE_NAME "/data/local/tmp/mux.out"
+
+static BenchmarkTestEnvironment *gEnv = nullptr;
+
+class MuxerTest : public ::testing::TestWithParam<pair<string, string>> {};
+
+static MUXER_OUTPUT_T getMuxerOutFormat(string fmt) {
+ static const struct {
+ string name;
+ MUXER_OUTPUT_T value;
+ } kFormatMaps[] = {{"mp4", MUXER_OUTPUT_FORMAT_MPEG_4},
+ {"webm", MUXER_OUTPUT_FORMAT_WEBM},
+ {"3gpp", MUXER_OUTPUT_FORMAT_3GPP},
+ {"ogg", MUXER_OUTPUT_FORMAT_OGG}};
+
+ MUXER_OUTPUT_T format = MUXER_OUTPUT_FORMAT_INVALID;
+ for (size_t i = 0; i < sizeof(kFormatMaps) / sizeof(kFormatMaps[0]); ++i) {
+ if (!fmt.compare(kFormatMaps[i].name)) {
+ format = kFormatMaps[i].value;
+ break;
+ }
+ }
+ return format;
+}
+
+TEST_P(MuxerTest, Mux) {
+ 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;
+ }
+ 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;
+ }
+
+ Muxer *muxerObj = new Muxer();
+ Extractor *extractor = muxerObj->getExtractor();
+ if (!extractor) {
+ cout << "[ WARN ] Test Skipped. Extractor creation failed \n";
+ return;
+ }
+
+ // Read file properties
+ size_t fileSize = 0;
+ fseek(inputFp, 0, SEEK_END);
+ fileSize = ftell(inputFp);
+ fseek(inputFp, 0, SEEK_SET);
+ int32_t fd = fileno(inputFp);
+
+ int32_t trackCount = extractor->initExtractor(fd, fileSize);
+ if (trackCount <= 0) {
+ cout << "[ WARN ] Test Skipped. initExtractor failed\n";
+ return;
+ }
+
+ 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;
+ }
+
+ uint8_t *inputBuffer = (uint8_t *)malloc(kMaxBufferSize);
+ if (!inputBuffer) {
+ std::cout << "[ WARN ] Test Skipped. Insufficient memory \n";
+ return;
+ }
+ // AMediaCodecBufferInfo : <size of frame> <flags> <presentationTimeUs> <offset>
+ 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 > kMaxBufferSize) {
+ cout << "[ WARN ] Test Skipped. Memory allocated not sufficient\n";
+ free(inputBuffer);
+ return;
+ }
+ memcpy(inputBuffer + inputBufferOffset, extractor->getFrameBuf(), info.size);
+ info.offset = inputBufferOffset;
+ frameInfos.push_back(info);
+ inputBufferOffset += info.size;
+ }
+
+ 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;
+ }
+ int32_t fd = fileno(outputFp);
+ status = muxerObj->initMuxer(fd, outputFormat);
+ if (status != 0) {
+ cout << "[ WARN ] Test Skipped. initMuxer failed\n";
+ return;
+ }
+
+ status = muxerObj->mux(inputBuffer, frameInfos);
+ if (status != 0) {
+ cout << "[ WARN ] Test Skipped. Mux failed \n";
+ return;
+ }
+ muxerObj->deInitMuxer();
+ muxerObj->dumpStatistics(GetParam().first + "." + fmt.c_str());
+ free(inputBuffer);
+ fclose(outputFp);
+ muxerObj->resetMuxer();
+ }
+ fclose(inputFp);
+ extractor->deInitExtractor();
+ delete muxerObj;
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ MuxerTestAll, MuxerTest,
+ ::testing::Values(make_pair("crowd_1920x1080_25fps_4000kbps_vp8.webm", "webm"),
+ make_pair("crowd_1920x1080_25fps_4000kbps_vp9.webm", "webm"),
+ make_pair("crowd_1920x1080_25fps_6000kbps_mpeg4.mp4", "mp4"),
+ make_pair("crowd_352x288_25fps_6000kbps_h263.3gp", "mp4"),
+ make_pair("crowd_1920x1080_25fps_6700kbps_h264.ts", "mp4"),
+ make_pair("crowd_1920x1080_25fps_4000kbps_h265.mkv", "mp4"),
+ make_pair("crowd_1920x1080_25fps_6000kbps_mpeg4.mp4", "3gpp"),
+ make_pair("crowd_352x288_25fps_6000kbps_h263.3gp", "3gpp"),
+ make_pair("crowd_1920x1080_25fps_6700kbps_h264.ts", "3gpp"),
+ make_pair("crowd_1920x1080_25fps_4000kbps_h265.mkv", "3gpp"),
+ make_pair("bbb_48000hz_2ch_100kbps_opus_5mins.webm", "ogg"),
+ make_pair("bbb_44100hz_2ch_80kbps_vorbis_5mins.mp4", "webm"),
+ make_pair("bbb_48000hz_2ch_100kbps_opus_5mins.webm", "webm"),
+ make_pair("bbb_44100hz_2ch_128kbps_aac_5mins.mp4", "mp4"),
+ make_pair("bbb_8000hz_1ch_8kbps_amrnb_5mins.3gp", "mp4"),
+ make_pair("bbb_16000hz_1ch_9kbps_amrwb_5mins.3gp", "mp4"),
+ make_pair("bbb_44100hz_2ch_128kbps_aac_5mins.mp4", "3gpp"),
+ make_pair("bbb_8000hz_1ch_8kbps_amrnb_5mins.3gp", "3gpp"),
+ make_pair("bbb_16000hz_1ch_9kbps_amrwb_5mins.3gp", "3gpp")));
+
+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("Test result = %d\n", status);
+ }
+ return status;
+}
diff --git a/media/utils/Android.bp b/media/utils/Android.bp
index 0ed92f7..2dbc476 100644
--- a/media/utils/Android.bp
+++ b/media/utils/Android.bp
@@ -45,10 +45,10 @@
"-Werror",
],
- include_dirs: [
- // For android_mallopt definitions.
- "bionic/libc/private"
+ header_libs: [
+ "bionic_libc_platform_headers",
],
+
local_include_dirs: ["include"],
export_include_dirs: ["include"],
}
diff --git a/media/utils/MemoryLeakTrackUtil.cpp b/media/utils/MemoryLeakTrackUtil.cpp
index 2988b52..6166859 100644
--- a/media/utils/MemoryLeakTrackUtil.cpp
+++ b/media/utils/MemoryLeakTrackUtil.cpp
@@ -22,7 +22,7 @@
#include "media/MemoryLeakTrackUtil.h"
#include <sstream>
-#include <bionic_malloc.h>
+#include <bionic/malloc.h>
/*
* The code here originally resided in MediaPlayerService.cpp
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 1f8ceec..14d6178 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -5098,7 +5098,7 @@
if (invalidate) continue;
for (auto client : desc->clientsList(false /*activeOnly*/)) {
- if (!desc->mProfile->isDirectOutput()) {
+ if (desc->isDuplicated() || !desc->mProfile->isDirectOutput()) {
// a client on a non direct outputs has necessarily a linear PCM format
// so we can call selectOutput() safely
const audio_io_handle_t newOutput = selectOutput(dstOutputs,
diff --git a/services/camera/libcameraservice/api2/HeicCompositeStream.cpp b/services/camera/libcameraservice/api2/HeicCompositeStream.cpp
index 052112a..410fd2c 100644
--- a/services/camera/libcameraservice/api2/HeicCompositeStream.cpp
+++ b/services/camera/libcameraservice/api2/HeicCompositeStream.cpp
@@ -1210,7 +1210,7 @@
outputFormat->setInt32(KEY_I_FRAME_INTERVAL, 0);
outputFormat->setInt32(KEY_COLOR_FORMAT,
useGrid ? COLOR_FormatYUV420Flexible : COLOR_FormatSurface);
- outputFormat->setInt32(KEY_FRAME_RATE, gridRows * gridCols);
+ outputFormat->setInt32(KEY_FRAME_RATE, useGrid ? gridRows * gridCols : kNoGridOpRate);
// This only serves as a hint to encoder when encoding is not real-time.
outputFormat->setInt32(KEY_OPERATING_RATE, useGrid ? kGridOpRate : kNoGridOpRate);
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index dd5a62b..3188892 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -2802,6 +2802,27 @@
mOperatingMode = operatingMode;
}
+ // In case called from configureStreams, abort queued input buffers not belonging to
+ // any pending requests.
+ if (mInputStream != NULL && notifyRequestThread) {
+ while (true) {
+ camera3_stream_buffer_t inputBuffer;
+ status_t res = mInputStream->getInputBuffer(&inputBuffer,
+ /*respectHalLimit*/ false);
+ if (res != OK) {
+ // Exhausted acquiring all input buffers.
+ break;
+ }
+
+ inputBuffer.status = CAMERA3_BUFFER_STATUS_ERROR;
+ res = mInputStream->returnInputBuffer(inputBuffer);
+ if (res != OK) {
+ ALOGE("%s: %d: couldn't return input buffer while clearing input queue: "
+ "%s (%d)", __FUNCTION__, __LINE__, strerror(-res), res);
+ }
+ }
+ }
+
if (!mNeedConfig) {
ALOGV("%s: Skipping config, no stream changes", __FUNCTION__);
return OK;
@@ -5084,6 +5105,7 @@
ALOGW("%s: %d: couldn't get input buffer while clearing the request "
"list: %s (%d)", __FUNCTION__, __LINE__, strerror(-res), res);
} else {
+ inputBuffer.status = CAMERA3_BUFFER_STATUS_ERROR;
res = (*it)->mInputStream->returnInputBuffer(inputBuffer);
if (res != OK) {
ALOGE("%s: %d: couldn't return input buffer while clearing the request "
diff --git a/services/camera/libcameraservice/device3/Camera3InputStream.cpp b/services/camera/libcameraservice/device3/Camera3InputStream.cpp
index fc83684..cb59a76 100644
--- a/services/camera/libcameraservice/device3/Camera3InputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3InputStream.cpp
@@ -71,7 +71,8 @@
res = mConsumer->acquireBuffer(&bufferItem, /*waitForFence*/false);
if (res != OK) {
- ALOGE("%s: Stream %d: Can't acquire next output buffer: %s (%d)",
+ // This may or may not be an error condition depending on caller.
+ ALOGV("%s: Stream %d: Can't acquire next output buffer: %s (%d)",
__FUNCTION__, mId, strerror(-res), res);
return res;
}