Merge changes I3f135b98,Ifadb1d24,Ieef3e05d,Id342c224
* changes:
audio policy: fix getDevicesForStream() with preferred route
audiopolicy: refactor playback activity ref counting
audio policy: remove AudioSession class
audio policy: refactor preferred route implementation
diff --git a/media/libmediaplayer2/Android.bp b/media/libmediaplayer2/Android.bp
index 300adec..0672ca9 100644
--- a/media/libmediaplayer2/Android.bp
+++ b/media/libmediaplayer2/Android.bp
@@ -60,7 +60,7 @@
"libprotobuf-cpp-lite",
"libstagefright_nuplayer2",
"libstagefright_rtsp",
- "libstagefright_timedtext",
+ "libstagefright_timedtext2",
],
export_include_dirs: [
diff --git a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h
index 715bbe0..55d812b 100644
--- a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h
+++ b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h
@@ -60,7 +60,8 @@
class MediaPlayer2InterfaceListener: public RefBase
{
public:
- virtual void notify(int64_t srcId, int msg, int ext1, int ext2, const Parcel *obj) = 0;
+ virtual void notify(int64_t srcId, int msg, int ext1, int ext2,
+ const PlayerMessage *obj) = 0;
};
class MediaPlayer2Interface : public AHandler {
@@ -240,7 +241,7 @@
mListener = listener;
}
- void sendEvent(int64_t srcId, int msg, int ext1=0, int ext2=0, const Parcel *obj=NULL) {
+ void sendEvent(int64_t srcId, int msg, int ext1=0, int ext2=0, const PlayerMessage *obj=NULL) {
sp<MediaPlayer2InterfaceListener> listener;
{
Mutex::Autolock autoLock(mListenerLock);
diff --git a/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h b/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h
index 628e732..e03b499 100644
--- a/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h
+++ b/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h
@@ -42,7 +42,8 @@
class MediaPlayer2Listener: virtual public RefBase
{
public:
- virtual void notify(int64_t srcId, int msg, int ext1, int ext2, const Parcel *obj) = 0;
+ virtual void notify(int64_t srcId, int msg, int ext1, int ext2,
+ const PlayerMessage *obj = NULL) = 0;
};
class MediaPlayer2 : public MediaPlayer2InterfaceListener
@@ -89,7 +90,7 @@
bool isLooping();
status_t setVolume(float leftVolume, float rightVolume);
void notify(int64_t srcId, int msg, int ext1, int ext2,
- const Parcel *obj = NULL);
+ const PlayerMessage *obj = NULL);
status_t invoke(const PlayerMessage &request, PlayerMessage *reply);
status_t setMetadataFilter(const Parcel& filter);
status_t getMetadata(bool update_only, bool apply_filter, Parcel *metadata);
diff --git a/media/libmediaplayer2/mediaplayer2.cpp b/media/libmediaplayer2/mediaplayer2.cpp
index 18cf040..8d0df8a 100644
--- a/media/libmediaplayer2/mediaplayer2.cpp
+++ b/media/libmediaplayer2/mediaplayer2.cpp
@@ -223,7 +223,8 @@
~proxyListener() { };
- virtual void notify(int64_t srcId, int msg, int ext1, int ext2, const Parcel *obj) override {
+ virtual void notify(int64_t srcId, int msg, int ext1, int ext2,
+ const PlayerMessage *obj) override {
sp<MediaPlayer2> player = mPlayer.promote();
if (player != NULL) {
player->notify(srcId, msg, ext1, ext2, obj);
@@ -1271,7 +1272,7 @@
}
}
-void MediaPlayer2::notify(int64_t srcId, int msg, int ext1, int ext2, const Parcel *obj) {
+void MediaPlayer2::notify(int64_t srcId, int msg, int ext1, int ext2, const PlayerMessage *obj) {
ALOGV("message received srcId=%lld, msg=%d, ext1=%d, ext2=%d",
(long long)srcId, msg, ext1, ext2);
diff --git a/media/libmediaplayer2/nuplayer2/GenericSource2.cpp b/media/libmediaplayer2/nuplayer2/GenericSource2.cpp
index e317e23..4ce1a88 100644
--- a/media/libmediaplayer2/nuplayer2/GenericSource2.cpp
+++ b/media/libmediaplayer2/nuplayer2/GenericSource2.cpp
@@ -1590,15 +1590,20 @@
return OK; // source without DRM info
}
- sp<ABuffer> drmInfoBuffer = NuPlayer2Drm::retrieveDrmInfo(psshInfo);
- ALOGV("checkDrmInfo: MEDIA_DRM_INFO PSSH drm info size: %d", (int)drmInfoBuffer->size());
+ PlayerMessage playerMsg;
+ status_t ret = NuPlayer2Drm::retrieveDrmInfo(psshInfo, &playerMsg);
+ ALOGV("checkDrmInfo: MEDIA_DRM_INFO PSSH drm info size: %d", (int)playerMsg.ByteSize());
- if (drmInfoBuffer->size() == 0) {
- ALOGE("checkDrmInfo: Unexpected parcel size: 0");
+ if (ret != OK) {
+ ALOGE("checkDrmInfo: failed to retrive DrmInfo %d", ret);
return UNKNOWN_ERROR;
}
- notifyDrmInfo(drmInfoBuffer);
+ int size = playerMsg.ByteSize();
+ sp<ABuffer> drmInfoBuf = new ABuffer(size);
+ playerMsg.SerializeToArray(drmInfoBuf->data(), size);
+ drmInfoBuf->setRange(0, size);
+ notifyDrmInfo(drmInfoBuf);
return OK;
}
diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp
index 2a0fbdd..2f90825 100644
--- a/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp
+++ b/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp
@@ -33,7 +33,7 @@
#include "NuPlayer2Source.h"
#include "RTSPSource2.h"
#include "GenericSource2.h"
-#include "TextDescriptions.h"
+#include "TextDescriptions2.h"
#include "ATSParser.h"
@@ -2145,7 +2145,8 @@
displayHeight);
}
-void NuPlayer2::notifyListener(int64_t srcId, int msg, int ext1, int ext2, const Parcel *in) {
+void NuPlayer2::notifyListener(
+ int64_t srcId, int msg, int ext1, int ext2, const PlayerMessage *in) {
if (mDriver == NULL) {
return;
}
@@ -2616,15 +2617,15 @@
// Modular DRM
case Source::kWhatDrmInfo:
{
- Parcel parcel;
+ PlayerMessage playerMsg;
sp<ABuffer> drmInfo;
CHECK(msg->findBuffer("drmInfo", &drmInfo));
- parcel.setData(drmInfo->data(), drmInfo->size());
+ playerMsg.ParseFromArray(drmInfo->data(), drmInfo->size());
- ALOGV("onSourceNotify() kWhatDrmInfo MEDIA2_DRM_INFO drmInfo: %p parcel size: %zu",
- drmInfo.get(), parcel.dataSize());
+ ALOGV("onSourceNotify() kWhatDrmInfo MEDIA2_DRM_INFO drmInfo: %p playerMsg size: %d",
+ drmInfo.get(), playerMsg.ByteSize());
- notifyListener(srcId, MEDIA2_DRM_INFO, 0 /* ext1 */, 0 /* ext2 */, &parcel);
+ notifyListener(srcId, MEDIA2_DRM_INFO, 0 /* ext1 */, 0 /* ext2 */, &playerMsg);
break;
}
@@ -2845,35 +2846,31 @@
CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
CHECK(buffer->meta()->findInt64("durationUs", &durationUs));
- Parcel in;
- in.writeInt32(trackIndex + baseIndex);
- in.writeInt64(timeUs);
- in.writeInt64(durationUs);
- in.writeInt32(buffer->size());
- in.writeInt32(buffer->size());
- in.write(buffer->data(), buffer->size());
+ PlayerMessage playerMsg;
+ playerMsg.add_values()->set_int32_value(trackIndex + baseIndex);
+ playerMsg.add_values()->set_int64_value(timeUs);
+ playerMsg.add_values()->set_int64_value(durationUs);
+ playerMsg.add_values()->set_bytes_value(buffer->data(), buffer->size());
- notifyListener(mSrcId, MEDIA2_SUBTITLE_DATA, 0, 0, &in);
+ notifyListener(mSrcId, MEDIA2_SUBTITLE_DATA, 0, 0, &playerMsg);
}
void NuPlayer2::sendTimedMetaData(const sp<ABuffer> &buffer) {
int64_t timeUs;
CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
- Parcel in;
- in.writeInt64(timeUs);
- in.writeInt32(buffer->size());
- in.writeInt32(buffer->size());
- in.write(buffer->data(), buffer->size());
+ PlayerMessage playerMsg;
+ playerMsg.add_values()->set_int64_value(timeUs);
+ playerMsg.add_values()->set_bytes_value(buffer->data(), buffer->size());
- notifyListener(mSrcId, MEDIA2_META_DATA, 0, 0, &in);
+ notifyListener(mSrcId, MEDIA2_META_DATA, 0, 0, &playerMsg);
}
void NuPlayer2::sendTimedTextData(const sp<ABuffer> &buffer) {
const void *data;
size_t size = 0;
int64_t timeUs;
- int32_t flag = TextDescriptions::IN_BAND_TEXT_3GPP;
+ int32_t flag = TextDescriptions2::IN_BAND_TEXT_3GPP;
AString mime;
CHECK(buffer->meta()->findString("mime", &mime));
@@ -2882,21 +2879,21 @@
data = buffer->data();
size = buffer->size();
- Parcel parcel;
+ PlayerMessage playerMsg;
if (size > 0) {
CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
int32_t global = 0;
if (buffer->meta()->findInt32("global", &global) && global) {
- flag |= TextDescriptions::GLOBAL_DESCRIPTIONS;
+ flag |= TextDescriptions2::GLOBAL_DESCRIPTIONS;
} else {
- flag |= TextDescriptions::LOCAL_DESCRIPTIONS;
+ flag |= TextDescriptions2::LOCAL_DESCRIPTIONS;
}
- TextDescriptions::getParcelOfDescriptions(
- (const uint8_t *)data, size, flag, timeUs / 1000, &parcel);
+ TextDescriptions2::getPlayerMessageOfDescriptions(
+ (const uint8_t *)data, size, flag, timeUs / 1000, &playerMsg);
}
- if ((parcel.dataSize() > 0)) {
- notifyListener(mSrcId, MEDIA2_TIMED_TEXT, 0, 0, &parcel);
+ if (playerMsg.values_size() > 0) {
+ notifyListener(mSrcId, MEDIA2_TIMED_TEXT, 0, 0, &playerMsg);
} else { // send an empty timed text
notifyListener(mSrcId, MEDIA2_TIMED_TEXT, 0, 0);
}
diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2.h b/media/libmediaplayer2/nuplayer2/NuPlayer2.h
index c92d408..77845ac 100644
--- a/media/libmediaplayer2/nuplayer2/NuPlayer2.h
+++ b/media/libmediaplayer2/nuplayer2/NuPlayer2.h
@@ -296,7 +296,7 @@
const sp<AMessage> &inputFormat,
const sp<AMessage> &outputFormat = NULL);
- void notifyListener(int64_t srcId, int msg, int ext1, int ext2, const Parcel *in = NULL);
+ void notifyListener(int64_t srcId, int msg, int ext1, int ext2, const PlayerMessage *in = NULL);
void handleFlushComplete(bool audio, bool isDecoder);
void finishFlushIfPossible();
diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp
index fd231a3..4d799b8 100644
--- a/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp
+++ b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp
@@ -44,40 +44,39 @@
namespace android {
-struct ParcelWrapper : public RefBase {
- static sp<ParcelWrapper> Create(const Parcel *p) {
+struct PlayerMessageWrapper : public RefBase {
+ static sp<PlayerMessageWrapper> Create(const PlayerMessage *p) {
if (p != NULL) {
- sp<ParcelWrapper> pw = new ParcelWrapper();
- if (pw->appendFrom(p) == OK) {
- return pw;
- }
+ sp<PlayerMessageWrapper> pw = new PlayerMessageWrapper();
+ pw->copyFrom(p);
+ return pw;
}
return NULL;
}
- const Parcel *getParcel() {
- return mParcel;
+ const PlayerMessage *getPlayerMessage() {
+ return mPlayerMessage;
}
protected:
- virtual ~ParcelWrapper() {
- if (mParcel != NULL) {
- delete mParcel;
+ virtual ~PlayerMessageWrapper() {
+ if (mPlayerMessage != NULL) {
+ delete mPlayerMessage;
}
}
private:
- ParcelWrapper()
- : mParcel(NULL) { }
+ PlayerMessageWrapper()
+ : mPlayerMessage(NULL) { }
- status_t appendFrom(const Parcel *p) {
- if (mParcel == NULL) {
- mParcel = new Parcel;
+ void copyFrom(const PlayerMessage *p) {
+ if (mPlayerMessage == NULL) {
+ mPlayerMessage = new PlayerMessage;
}
- return mParcel->appendFrom(p, 0 /* start */, p->dataSize());
+ mPlayerMessage->CopyFrom(*p);
}
- Parcel *mParcel;
+ PlayerMessage *mPlayerMessage;
};
// key for media statistics
@@ -828,12 +827,12 @@
CHECK(msg->findInt32("messageId", &msgId));
msg->findInt32("ext1", &ext1);
msg->findInt32("ext2", &ext2);
- sp<ParcelWrapper> in;
+ sp<PlayerMessageWrapper> in;
sp<RefBase> obj;
- if (msg->findObject("parcel", &obj) && obj != NULL) {
- in = static_cast<ParcelWrapper *>(obj.get());
+ if (msg->findObject("obj", &obj) && obj != NULL) {
+ in = static_cast<PlayerMessageWrapper *>(obj.get());
}
- sendEvent(srcId, msgId, ext1, ext2, (in == NULL ? NULL : in->getParcel()));
+ sendEvent(srcId, msgId, ext1, ext2, (in == NULL ? NULL : in->getPlayerMessage()));
break;
}
default:
@@ -842,16 +841,16 @@
}
void NuPlayer2Driver::notifyListener(
- int64_t srcId, int msg, int ext1, int ext2, const Parcel *in) {
+ int64_t srcId, int msg, int ext1, int ext2, const PlayerMessage *in) {
Mutex::Autolock autoLock(mLock);
notifyListener_l(srcId, msg, ext1, ext2, in);
}
void NuPlayer2Driver::notifyListener_l(
- int64_t srcId, int msg, int ext1, int ext2, const Parcel *in) {
+ int64_t srcId, int msg, int ext1, int ext2, const PlayerMessage *in) {
ALOGD("notifyListener_l(%p), (%lld, %d, %d, %d, %d), loop setting(%d, %d)",
this, (long long)srcId, msg, ext1, ext2,
- (in == NULL ? -1 : (int)in->dataSize()), mAutoLoop, mLooping);
+ (in == NULL ? -1 : (int)in->ByteSize()), mAutoLoop, mLooping);
if (srcId == mSrcId) {
switch (msg) {
case MEDIA2_PLAYBACK_COMPLETE:
@@ -918,7 +917,7 @@
notify->setInt32("messageId", msg);
notify->setInt32("ext1", ext1);
notify->setInt32("ext2", ext2);
- notify->setObject("parcel", ParcelWrapper::Create(in));
+ notify->setObject("obj", PlayerMessageWrapper::Create((PlayerMessage*)in));
notify->post();
}
diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.h b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.h
index 8dfcaa1..50ee173 100644
--- a/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.h
+++ b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.h
@@ -78,7 +78,7 @@
void notifyRebufferingWhenExit(int64_t srcId, bool status);
void notifySeekComplete(int64_t srcId);
void notifyListener(int64_t srcId, int msg, int ext1 = 0, int ext2 = 0,
- const Parcel *in = NULL);
+ const PlayerMessage *in = NULL);
void notifyFlagsChanged(int64_t srcId, uint32_t flags);
// Modular DRM
@@ -145,7 +145,7 @@
status_t start_l();
void notifyListener_l(int64_t srcId, int msg, int ext1 = 0, int ext2 = 0,
- const Parcel *in = NULL);
+ const PlayerMessage *in = NULL);
DISALLOW_EVIL_CONSTRUCTORS(NuPlayer2Driver);
};
diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2Drm.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2Drm.cpp
index 4853ae1..57d1147 100644
--- a/media/libmediaplayer2/nuplayer2/NuPlayer2Drm.cpp
+++ b/media/libmediaplayer2/nuplayer2/NuPlayer2Drm.cpp
@@ -133,9 +133,8 @@
return drmInfoBuffer;
}
-sp<ABuffer> NuPlayer2Drm::retrieveDrmInfo(PsshInfo *psshInfo)
+status_t NuPlayer2Drm::retrieveDrmInfo(PsshInfo *psshInfo, PlayerMessage *playerMsg)
{
-
std::ostringstream pssh, drmInfo;
// 0) Generate PSSH bytes
@@ -153,22 +152,20 @@
ALOGV("retrieveDrmInfo: MEDIA_DRM_INFO PSSH: size: %u %s", psshSize, psshHex);
// 1) Write PSSH bytes
- drmInfo.write(reinterpret_cast<const char *>(&psshSize), sizeof(psshSize));
- drmInfo.write(reinterpret_cast<const char *>(pssh.str().c_str()), psshSize);
+ playerMsg->add_values()->set_bytes_value(
+ reinterpret_cast<const char *>(pssh.str().c_str()), psshSize);
// 2) Write supportedDRMs
uint32_t numentries = psshInfo->numentries;
- drmInfo.write(reinterpret_cast<const char *>(&numentries), sizeof(numentries));
+ playerMsg->add_values()->set_int32_value(numentries);
for (size_t i = 0; i < numentries; i++) {
PsshEntry *entry = &psshInfo->entries[i];
- drmInfo.write(reinterpret_cast<const char *>(&entry->uuid), sizeof(entry->uuid));
+ playerMsg->add_values()->set_bytes_value(
+ reinterpret_cast<const char *>(&entry->uuid), sizeof(entry->uuid));
ALOGV("retrieveDrmInfo: MEDIA_DRM_INFO supportedScheme[%zu] %s", i,
DrmUUID::arrayToHex((const uint8_t*)&entry->uuid, sizeof(AMediaUUID)).string());
}
-
- sp<ABuffer> drmInfoBuf = ABuffer::CreateAsCopy(drmInfo.str().c_str(), drmInfo.tellp());
- drmInfoBuf->setRange(0, drmInfo.tellp());
- return drmInfoBuf;
+ return OK;
}
} // namespace android
diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2Drm.h b/media/libmediaplayer2/nuplayer2/NuPlayer2Drm.h
index 99d2415..968d1be 100644
--- a/media/libmediaplayer2/nuplayer2/NuPlayer2Drm.h
+++ b/media/libmediaplayer2/nuplayer2/NuPlayer2Drm.h
@@ -23,6 +23,10 @@
#include <utils/String8.h>
#include <utils/Vector.h>
+#include "mediaplayer2.pb.h"
+
+using android::media::MediaPlayer2Proto::PlayerMessage;
+
namespace android {
struct DrmUUID {
@@ -80,7 +84,7 @@
public:
static sp<ABuffer> retrieveDrmInfo(const void *pssh, uint32_t psshsize);
- static sp<ABuffer> retrieveDrmInfo(PsshInfo *);
+ static status_t retrieveDrmInfo(PsshInfo *, PlayerMessage *);
}; // NuPlayer2Drm
diff --git a/media/libnblog/Android.bp b/media/libnblog/Android.bp
index 74aaf77..4a4aac1 100644
--- a/media/libnblog/Android.bp
+++ b/media/libnblog/Android.bp
@@ -13,9 +13,14 @@
"libbinder",
"libcutils",
"liblog",
+ "libmediametrics",
"libutils",
],
+ static_libs: [
+ "libjsoncpp",
+ ],
+
cflags: [
"-Werror",
"-Wall",
diff --git a/media/libnblog/NBLog.cpp b/media/libnblog/NBLog.cpp
index f73e2ae..aee0ea3 100644
--- a/media/libnblog/NBLog.cpp
+++ b/media/libnblog/NBLog.cpp
@@ -21,6 +21,7 @@
#include <algorithm>
#include <climits>
#include <math.h>
+#include <memory>
#include <unordered_set>
#include <vector>
#include <stdarg.h>
@@ -31,12 +32,14 @@
#include <time.h>
#include <new>
#include <audio_utils/roundup.h>
+#include <json/json.h>
#include <media/nblog/NBLog.h>
#include <media/nblog/PerformanceAnalysis.h>
#include <media/nblog/ReportPerformance.h>
#include <utils/CallStack.h>
#include <utils/Log.h>
#include <utils/String8.h>
+#include <utils/Timers.h>
#include <queue>
#include <utility>
@@ -417,7 +420,7 @@
if (!mEnabled) {
return;
}
- int64_t ts = get_monotonic_ns();
+ const nsecs_t ts = systemTime();
if (ts > 0) {
log(EVENT_FMT_TIMESTAMP, &ts, sizeof(ts));
} else {
@@ -432,7 +435,7 @@
}
HistTsEntry data;
data.hash = hash;
- data.ts = get_monotonic_ns();
+ data.ts = systemTime();
if (data.ts > 0) {
log(event, &data, sizeof(data));
} else {
@@ -780,15 +783,17 @@
return snapshot;
}
+// TODO separate this method from the rest of NBLog
// Takes raw content of the local merger FIFO, processes log entries, and
// writes the data to a map of class PerformanceAnalysis, based on their thread ID.
-void NBLog::MergeReader::getAndProcessSnapshot(NBLog::Snapshot &snapshot, int author)
+void NBLog::MergeReader::processSnapshot(NBLog::Snapshot &snapshot, int author)
{
+ ReportPerformance::PerformanceData& data = mThreadPerformanceData[author];
// We don't do "auto it" because it reduces readability in this case.
for (EntryIterator it = snapshot.begin(); it != snapshot.end(); ++it) {
switch (it->type) {
case EVENT_HISTOGRAM_ENTRY_TS: {
- HistTsEntry payload = it.payload<HistTsEntry>();
+ const HistTsEntry payload = it.payload<HistTsEntry>();
// TODO: hash for histogram ts and audio state need to match
// and correspond to audio production source file location
mThreadPerformanceAnalysis[author][0 /*hash*/].logTsEntry(payload.ts);
@@ -796,15 +801,37 @@
case EVENT_AUDIO_STATE: {
mThreadPerformanceAnalysis[author][0 /*hash*/].handleStateChange();
} break;
+ case EVENT_THREAD_INFO: {
+ const thread_info_t info = it.payload<thread_info_t>();
+ // TODO make PerformanceData hold a type of thread_info_t.
+ // Currently, thread_info_t is defined in NBLog.h, which includes
+ // PerformanceAnalysis.h. PerformanceData is defined in PerformanceAnalysis.h,
+ // where including NBLog.h would result in circular includes. The organization
+ // of files will need to change to avoid this problem.
+ data.type = info.type;
+ data.frameCount = info.frameCount;
+ data.sampleRate = info.sampleRate;
+ } break;
case EVENT_LATENCY: {
- double latencyMs = it.payload<double>();
- mPerformanceData.addLatencyEntry(author, latencyMs);
+ const double latencyMs = it.payload<double>();
+ data.latencyHist.add(latencyMs);
} break;
case EVENT_WORK_TIME: {
- uint64_t monotonicNs = it.payload<uint64_t>();
+ const int64_t monotonicNs = it.payload<int64_t>();
const double monotonicMs = monotonicNs * 1e-6;
- mPerformanceData.addCycleTimeEntry(author, monotonicMs);
+ data.workHist.add(monotonicMs);
+ data.active += monotonicNs;
} break;
+ case EVENT_WARMUP_TIME: {
+ const double timeMs = it.payload<double>();
+ data.warmupHist.add(timeMs);
+ } break;
+ case EVENT_UNDERRUN:
+ data.underruns++;
+ break;
+ case EVENT_OVERRUN:
+ data.overruns++;
+ break;
case EVENT_RESERVED:
case EVENT_UPPER_BOUND:
ALOGW("warning: unexpected event %d", it->type);
@@ -826,7 +853,20 @@
// TODO unlock lock here
for (size_t i = 0; i < nLogs; i++) {
if (snapshots[i] != nullptr) {
- getAndProcessSnapshot(*(snapshots[i]), i);
+ processSnapshot(*(snapshots[i]), i);
+ }
+ }
+ checkPushToMediaMetrics();
+}
+
+void NBLog::MergeReader::checkPushToMediaMetrics()
+{
+ const nsecs_t now = systemTime();
+ for (auto& item : mThreadPerformanceData) {
+ ReportPerformance::PerformanceData& data = item.second;
+ if (now - data.start >= kPeriodicMediaMetricsPush) {
+ (void)ReportPerformance::sendToMediaMetrics(data);
+ data.reset(); // data is persistent per thread
}
}
}
@@ -835,7 +875,19 @@
{
// TODO: add a mutex around media.log dump
ReportPerformance::dump(fd, indent, mThreadPerformanceAnalysis);
- mPerformanceData.dump(fd);
+ Json::Value root(Json::arrayValue);
+ for (const auto& item : mThreadPerformanceData) {
+ const ReportPerformance::PerformanceData& data = item.second;
+ std::unique_ptr<Json::Value> threadData = ReportPerformance::dumpToJson(data);
+ if (threadData == nullptr) {
+ continue;
+ }
+ (*threadData)["threadNum"] = item.first;
+ root.append(*threadData);
+ }
+ Json::StyledWriter writer;
+ std::string rootStr = writer.write(root);
+ dprintf(fd, "%s", rootStr.c_str());
}
// TODO for future compatibility, would prefer to have a dump() go to string, and then go
@@ -856,13 +908,23 @@
it = handleFormat(FormatEntry(it), ×tamp, &body);
break;
case EVENT_WORK_TIME: {
- uint64_t monotonicNs = it.payload<uint64_t>();
- body.appendFormat("Thread cycle: %lu ns", (unsigned long)monotonicNs);
+ const int64_t monotonicNs = it.payload<int64_t>();
+ body.appendFormat("Thread cycle: %ld ns", (long)monotonicNs);
} break;
case EVENT_LATENCY: {
- double latencyMs = it.payload<double>();
+ const double latencyMs = it.payload<double>();
body.appendFormat("latency: %.3f ms", latencyMs);
} break;
+ case EVENT_WARMUP_TIME: {
+ const double timeMs = it.payload<double>();
+ body.appendFormat("warmup time: %.3f ms", timeMs);
+ } break;
+ case EVENT_UNDERRUN:
+ body.appendFormat("underrun");
+ break;
+ case EVENT_OVERRUN:
+ body.appendFormat("overrun");
+ break;
case EVENT_FMT_END:
case EVENT_RESERVED:
case EVENT_UPPER_BOUND:
@@ -947,7 +1009,7 @@
String8 *body)
{
// log timestamp
- int64_t ts = fmtEntry.timestamp();
+ const int64_t ts = fmtEntry.timestamp();
timestamp->clear();
timestamp->appendFormat("[%d.%03d]", (int) (ts / (1000 * 1000 * 1000)),
(int) ((ts / (1000 * 1000)) % 1000));
diff --git a/media/libnblog/PerformanceAnalysis.cpp b/media/libnblog/PerformanceAnalysis.cpp
index ce9e22a..ca9bc2c 100644
--- a/media/libnblog/PerformanceAnalysis.cpp
+++ b/media/libnblog/PerformanceAnalysis.cpp
@@ -41,12 +41,15 @@
#include <media/nblog/ReportPerformance.h>
#include <utils/Log.h>
#include <utils/String8.h>
+#include <utils/Timers.h>
#include <queue>
#include <utility>
namespace android {
+namespace ReportPerformance {
+
void Histogram::add(double value)
{
// TODO Handle domain and range error exceptions?
@@ -74,7 +77,7 @@
return mTotalCount;
}
-std::string Histogram::serializeToString() const {
+std::string Histogram::toString() const {
std::stringstream ss;
static constexpr char kDivider = '|';
ss << mBinSize << "," << mNumBins << "," << mLow << ",{";
@@ -104,42 +107,8 @@
return ss.str();
}
-// TODO make a hash map from Event type to std::pair<HistConfig, unordered_map<int, Histogram>>
-// so that we don't have to create a "add histogram entry" method for every different metric.
-void PerformanceData::addCycleTimeEntry(int author, double cycleTimeMs)
-{
- if (mCycleTimeMsHists.count(author) == 0) {
- mCycleTimeMsHists.emplace(author, Histogram(kCycleTimeConfig));
- }
- mCycleTimeMsHists.at(author).add(cycleTimeMs);
-}
-
-void PerformanceData::addLatencyEntry(int author, double latencyMs)
-{
- if (mLatencyMsHists.count(author) == 0) {
- mLatencyMsHists.emplace(author, Histogram(kLatencyConfig));
- }
- mLatencyMsHists.at(author).add(latencyMs);
-}
-
-void PerformanceData::dump(int fd, int indent __unused)
-{
- // TODO add thread metadata for better context.
- // Also output in a more machine-readable friendly format.
- dprintf(fd, "Thread cycle time histograms:\n");
- for (const auto &item : mCycleTimeMsHists) {
- dprintf(fd, " Thread %d: %s\n", item.first, item.second.serializeToString().c_str());
- }
- dprintf(fd, "Latency histograms:\n");
- for (const auto &item : mLatencyMsHists) {
- dprintf(fd, " Thread %d: %s\n", item.first, item.second.serializeToString().c_str());
- }
-}
-
//------------------------------------------------------------------------------
-namespace ReportPerformance {
-
// Given an audio processing wakeup timestamp, buckets the time interval
// since the previous timestamp into a histogram, searches for
// outliers, analyzes the outlier series for unexpectedly
diff --git a/media/libnblog/ReportPerformance.cpp b/media/libnblog/ReportPerformance.cpp
index 827e731..aab67c6 100644
--- a/media/libnblog/ReportPerformance.cpp
+++ b/media/libnblog/ReportPerformance.cpp
@@ -18,6 +18,7 @@
#include <fstream>
#include <iostream>
+#include <memory>
#include <queue>
#include <stdarg.h>
#include <stdint.h>
@@ -27,6 +28,8 @@
#include <sys/prctl.h>
#include <sys/time.h>
#include <utility>
+#include <json/json.h>
+#include <media/MediaAnalyticsItem.h>
#include <media/nblog/NBLog.h>
#include <media/nblog/PerformanceAnalysis.h>
#include <media/nblog/ReportPerformance.h>
@@ -37,13 +40,93 @@
namespace ReportPerformance {
+std::unique_ptr<Json::Value> dumpToJson(const PerformanceData& data)
+{
+ std::unique_ptr<Json::Value> rootPtr = std::make_unique<Json::Value>(Json::objectValue);
+ Json::Value& root = *rootPtr;
+ root["type"] = data.type;
+ root["frameCount"] = (Json::Value::Int)data.frameCount;
+ root["sampleRate"] = (Json::Value::Int)data.sampleRate;
+ root["workMsHist"] = data.workHist.toString();
+ root["latencyMsHist"] = data.latencyHist.toString();
+ root["warmupMsHist"] = data.warmupHist.toString();
+ root["underruns"] = (Json::Value::Int64)data.underruns;
+ root["overruns"] = (Json::Value::Int64)data.overruns;
+ root["activeMs"] = (Json::Value::Int64)ns2ms(data.active);
+ root["durationMs"] = (Json::Value::Int64)ns2ms(systemTime() - data.start);
+ return rootPtr;
+}
+
+bool sendToMediaMetrics(const PerformanceData& data)
+{
+ // See documentation for these metrics here:
+ // docs.google.com/document/d/11--6dyOXVOpacYQLZiaOY5QVtQjUyqNx2zT9cCzLKYE/edit?usp=sharing
+ static constexpr char kThreadType[] = "android.media.audiothread.type";
+ static constexpr char kThreadFrameCount[] = "android.media.audiothread.framecount";
+ static constexpr char kThreadSampleRate[] = "android.media.audiothread.samplerate";
+ static constexpr char kThreadWorkHist[] = "android.media.audiothread.workMs.hist";
+ static constexpr char kThreadLatencyHist[] = "android.media.audiothread.latencyMs.hist";
+ static constexpr char kThreadWarmupHist[] = "android.media.audiothread.warmupMs.hist";
+ static constexpr char kThreadUnderruns[] = "android.media.audiothread.underruns";
+ static constexpr char kThreadOverruns[] = "android.media.audiothread.overruns";
+ static constexpr char kThreadActive[] = "android.media.audiothread.activeMs";
+ static constexpr char kThreadDuration[] = "android.media.audiothread.durationMs";
+
+ std::unique_ptr<MediaAnalyticsItem> item(new MediaAnalyticsItem("audiothread"));
+
+ const Histogram &workHist = data.workHist;
+ if (workHist.totalCount() > 0) {
+ item->setCString(kThreadWorkHist, workHist.toString().c_str());
+ }
+
+ const Histogram &latencyHist = data.latencyHist;
+ if (latencyHist.totalCount() > 0) {
+ item->setCString(kThreadLatencyHist, latencyHist.toString().c_str());
+ }
+
+ const Histogram &warmupHist = data.warmupHist;
+ if (warmupHist.totalCount() > 0) {
+ item->setCString(kThreadWarmupHist, warmupHist.toString().c_str());
+ }
+
+ if (data.underruns > 0) {
+ item->setInt64(kThreadUnderruns, data.underruns);
+ }
+
+ if (data.overruns > 0) {
+ item->setInt64(kThreadOverruns, data.overruns);
+ }
+
+ // Send to Media Metrics if the record is not empty.
+ // The thread and time info are added inside the if statement because
+ // we want to send them only if there are performance metrics to send.
+ if (item->count() > 0) {
+ // Add thread info fields.
+ const int type = data.type;
+ // TODO have a int-to-string mapping defined somewhere else for other thread types.
+ if (type == 2) {
+ item->setCString(kThreadType, "FASTMIXER");
+ } else {
+ item->setCString(kThreadType, "UNKNOWN");
+ }
+ item->setInt32(kThreadFrameCount, data.frameCount);
+ item->setInt32(kThreadSampleRate, data.sampleRate);
+ // Add time info fields.
+ item->setInt64(kThreadActive, data.active / 1000000);
+ item->setInt64(kThreadDuration, (systemTime() - data.start) / 1000000);
+ return item->selfrecord();
+ }
+ return false;
+}
+
+//------------------------------------------------------------------------------
// TODO: use a function like this to extract logic from writeToFile
// https://stackoverflow.com/a/9279620
// Writes outlier intervals, timestamps, and histograms spanning long time intervals to file.
// TODO: write data in binary format
-void writeToFile(const std::deque<std::pair<timestamp, Histogram>> &hists,
+void writeToFile(const std::deque<std::pair<timestamp, Hist>> &hists,
const std::deque<std::pair<msInterval, timestamp>> &outlierData,
const std::deque<timestamp> &peakTimestamps,
const char * directory, bool append, int author, log_hash_t hash) {
diff --git a/media/libnblog/include/media/nblog/NBLog.h b/media/libnblog/include/media/nblog/NBLog.h
index 561e8c7..e43b026 100644
--- a/media/libnblog/include/media/nblog/NBLog.h
+++ b/media/libnblog/include/media/nblog/NBLog.h
@@ -20,6 +20,7 @@
#define ANDROID_MEDIA_NBLOG_H
#include <map>
+#include <memory>
#include <type_traits>
#include <unordered_set>
#include <vector>
@@ -30,6 +31,7 @@
#include <media/nblog/ReportPerformance.h>
#include <utils/Mutex.h>
#include <utils/threads.h>
+#include <utils/Timers.h>
namespace android {
@@ -39,7 +41,7 @@
public:
- using log_hash_t = ReportPerformance::log_hash_t;
+ using log_hash_t = uint64_t;
// FIXME Everything needed for client (writer API and registration) should be isolated
// from the rest of the implementation.
@@ -78,13 +80,29 @@
EVENT_AUDIO_STATE, // audio on/off event: logged on FastMixer::onStateChange call
// Types representing audio performance metrics
+ EVENT_THREAD_INFO, // thread type, frameCount and sampleRate, which give context
+ // for the metrics below.
EVENT_LATENCY, // difference between frames presented by HAL and frames
// written to HAL output sink, divided by sample rate.
EVENT_WORK_TIME, // the time a thread takes to do work, e.g. read, write, etc.
+ EVENT_WARMUP_TIME, // thread warmup time
+ EVENT_UNDERRUN, // predicted thread underrun event timestamp
+ EVENT_OVERRUN, // predicted thread overrun event timestamp
EVENT_UPPER_BOUND, // to check for invalid events
};
+ // NBLog custom-defined structs. Some NBLog Event types map to these structs.
+
+ // mapped from EVENT_THREAD_INFO
+ struct thread_info_t {
+ // TODO make type an enum
+ int type; // Thread type: 0 for MIXER, 1 for CAPTURE,
+ // 2 for FASTMIXER, 3 for FASTCAPTURE
+ size_t frameCount; // number of frames per read or write buffer
+ unsigned sampleRate; // in frames per second
+ };
+
template <Event E> struct get_mapped;
#define MAP_EVENT_TO_TYPE(E, T) \
template<> struct get_mapped<E> { \
@@ -95,8 +113,12 @@
}
// Maps an NBLog Event type to a C++ POD type.
+ MAP_EVENT_TO_TYPE(EVENT_THREAD_INFO, thread_info_t);
MAP_EVENT_TO_TYPE(EVENT_LATENCY, double);
- MAP_EVENT_TO_TYPE(EVENT_WORK_TIME, uint64_t);
+ MAP_EVENT_TO_TYPE(EVENT_WORK_TIME, int64_t);
+ MAP_EVENT_TO_TYPE(EVENT_WARMUP_TIME, double);
+ MAP_EVENT_TO_TYPE(EVENT_UNDERRUN, int64_t);
+ MAP_EVENT_TO_TYPE(EVENT_OVERRUN, int64_t);
private:
@@ -154,7 +176,7 @@
}
#else
template<typename T>
- inline T payload() {
+ inline T payload() const {
static_assert(std::is_trivially_copyable<T>::value
&& !std::is_pointer<T>::value,
"NBLog::EntryIterator payload must be trivially copyable, non-pointer type.");
@@ -418,6 +440,7 @@
void log(Event event, const void *data, size_t length);
void logvf(const char *fmt, va_list ap);
+
// helper functions for logging parts of a formatted entry
void logStart(const char *fmt);
void logTimestampFormat();
@@ -477,10 +500,12 @@
private:
// Amount of tries for reader to catch up with writer in getSnapshot().
static constexpr int kMaxObtainTries = 3;
+
// invalidBeginTypes and invalidEndTypes are used to align the Snapshot::begin() and
// Snapshot::end() EntryIterators to valid entries.
static const std::unordered_set<Event> invalidBeginTypes;
static const std::unordered_set<Event> invalidEndTypes;
+
// declared as const because audio_utils_fifo() constructor
sp<IMemory> mIMemory; // ref-counted version, assigned only in constructor
@@ -539,12 +564,14 @@
static void appendFloat(String8 *body, const void *data);
static void appendPID(String8 *body, const void *data, size_t length);
static void appendTimestamp(String8 *body, const void *data);
+
// The bufferDump functions are used for debugging only.
static String8 bufferDump(const uint8_t *buffer, size_t size);
static String8 bufferDump(const EntryIterator &it);
};
// ---------------------------------------------------------------------------
+ // TODO move Merger, MergeReader, and MergeThread to a separate file.
// This class is used to read data from each thread's individual FIFO in shared memory
// and write it to a single FIFO in local memory.
@@ -580,11 +607,17 @@
MergeReader(const void *shared, size_t size, Merger &merger);
void dump(int fd, int indent = 0);
+
// process a particular snapshot of the reader
- void getAndProcessSnapshot(Snapshot &snap, int author);
+ void processSnapshot(Snapshot &snap, int author);
+
// call getSnapshot of the content of the reader's buffer and process the data
void getAndProcessSnapshot();
+ // check for periodic push of performance data to media metrics, and perform
+ // the send if it is time to do so.
+ void checkPushToMediaMetrics();
+
private:
// FIXME Needs to be protected by a lock,
// because even though our use of it is read-only there may be asynchronous updates
@@ -596,7 +629,12 @@
// location within each author
ReportPerformance::PerformanceAnalysisMap mThreadPerformanceAnalysis;
- PerformanceData mPerformanceData;
+ // compresses and stores audio performance data from each thread's buffers.
+ // first parameter is author, i.e. thread index.
+ std::map<int, ReportPerformance::PerformanceData> mThreadPerformanceData;
+
+ // how often to push data to Media Metrics
+ static constexpr nsecs_t kPeriodicMediaMetricsPush = s2ns((nsecs_t)2 * 60 * 60); // 2 hours
// handle author entry by looking up the author's name and appending it to the body
// returns number of bytes read from fmtEntry
@@ -645,15 +683,6 @@
}; // class NBLog
-// TODO put somewhere else
-static inline int64_t get_monotonic_ns() {
- timespec ts;
- if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
- return (uint64_t) ts.tv_sec * 1000 * 1000 * 1000 + ts.tv_nsec;
- }
- return 0; // should not happen.
-}
-
} // namespace android
#endif // ANDROID_MEDIA_NBLOG_H
diff --git a/media/libnblog/include/media/nblog/PerformanceAnalysis.h b/media/libnblog/include/media/nblog/PerformanceAnalysis.h
index f2c3a48..80eddb1 100644
--- a/media/libnblog/include/media/nblog/PerformanceAnalysis.h
+++ b/media/libnblog/include/media/nblog/PerformanceAnalysis.h
@@ -19,15 +19,18 @@
#include <deque>
#include <map>
-#include <unordered_map>
+#include <utility>
#include <vector>
#include <media/nblog/ReportPerformance.h>
+#include <utils/Timers.h>
namespace android {
class String8;
+namespace ReportPerformance {
+
// TODO make this a templated class and put it in a separate file.
// The templated parameters would be bin size and low limit.
/*
@@ -106,7 +109,7 @@
*
* \return the histogram serialized as a string.
*/
- std::string serializeToString() const;
+ std::string toString() const;
private:
const double mBinSize; // Size of each bucket
@@ -119,38 +122,54 @@
uint64_t mTotalCount = 0; // Total number of values recorded
};
-// TODO For now this is a holder of audio performance metrics. The idea is essentially the same
-// as PerformanceAnalysis, but the design structure is different. There is a PerformanceAnalysis
-// instance for each thread writer (see PerformanceAnalysisMap later in this file), while a
-// PerformanceData instance already takes into account each thread writer in its calculations.
-// Eventually, this class should be merged with PerformanceAnalysis into some single entity.
-/*
- * PerformanceData stores audio performance data from audioflinger threads as histograms,
- * time series, or counts, and outputs them in a machine-readable format.
- */
-class PerformanceData {
-public:
- void addCycleTimeEntry(int author, double cycleTimeMs);
- void addLatencyEntry(int author, double latencyMs);
- void addWarmupTimeEntry(int author, double warmupTimeMs);
- void addWarmupCyclesEntry(int author, double warmupCycles);
- void dump(int fd, int indent = 0);
-private:
- // Values based on mUnderrunNs and mOverrunNs in FastMixer.cpp for frameCount = 192 and
- // mSampleRate = 48000, which correspond to 2 and 7 seconds.
- static constexpr Histogram::Config kCycleTimeConfig = { 0.25, 20, 2.};
- std::unordered_map<int /*author, i.e. thread number*/, Histogram> mCycleTimeMsHists;
+// This is essentially the same as class PerformanceAnalysis, but PerformanceAnalysis
+// also does some additional analyzing of data, while the purpose of this struct is
+// to hold data.
+struct PerformanceData {
+ // Values based on mUnderrunNs and mOverrunNs in FastMixer.cpp for frameCount = 192
+ // and mSampleRate = 48000, which correspond to 2 and 7 seconds.
+ static constexpr Histogram::Config kWorkConfig = { 0.25, 20, 2.};
// Values based on trial and error logging. Need a better way to determine
// bin size and lower/upper limits.
static constexpr Histogram::Config kLatencyConfig = { 2., 10, 10.};
- std::unordered_map<int, Histogram> mLatencyMsHists;
+
+ // Values based on trial and error logging. Need a better way to determine
+ // bin size and lower/upper limits.
+ static constexpr Histogram::Config kWarmupConfig = { 5., 10, 10.};
+
+ // Thread Info
+ // TODO make type an enum
+ int type = -1; // Thread type: 0 for MIXER, 1 for CAPTURE,
+ // 2 for FASTMIXER, 3 for FASTCAPTURE
+ size_t frameCount = 0;
+ unsigned sampleRate = 0;
+
+ // Performance Data
+ Histogram workHist{kWorkConfig};
+ Histogram latencyHist{kLatencyConfig};
+ Histogram warmupHist{kWarmupConfig};
+ int64_t underruns = 0;
+ int64_t overruns = 0;
+ nsecs_t active = 0;
+ nsecs_t start{systemTime()};
+
+ // Reset the performance data. This does not represent a thread state change.
+ // Thread info is not reset here because the data is meant to be a continuation of the thread
+ // that struct PerformanceData is associated with.
+ void reset() {
+ workHist.clear();
+ latencyHist.clear();
+ warmupHist.clear();
+ underruns = 0;
+ overruns = 0;
+ active = 0;
+ start = systemTime();
+ }
};
//------------------------------------------------------------------------------
-namespace ReportPerformance {
-
class PerformanceAnalysis;
// a map of PerformanceAnalysis instances
@@ -204,7 +223,7 @@
std::deque<timestamp> mPeakTimestamps;
// stores buffer period histograms with timestamp of first sample
- std::deque<std::pair<timestamp, Histogram>> mHists;
+ std::deque<std::pair<timestamp, Hist>> mHists;
// Parameters used when detecting outliers
struct BufferPeriod {
diff --git a/media/libnblog/include/media/nblog/ReportPerformance.h b/media/libnblog/include/media/nblog/ReportPerformance.h
index 1b11197..09bb2a0 100644
--- a/media/libnblog/include/media/nblog/ReportPerformance.h
+++ b/media/libnblog/include/media/nblog/ReportPerformance.h
@@ -19,19 +19,38 @@
#include <deque>
#include <map>
+#include <memory>
#include <vector>
+namespace Json {
+class Value;
+}
+
namespace android {
namespace ReportPerformance {
+struct PerformanceData;
+
+// Dumps performance data to a JSON format.
+// Return by pointer instead of by value to avoid dependency side effects of including
+// the header of an external library.
+std::unique_ptr<Json::Value> dumpToJson(const PerformanceData& data);
+
+// Send one thread's data to media metrics, if the performance data is nontrivial (i.e. not
+// all zero values). Return true if data was sent, false if there is nothing to write
+// or an error occurred while writing.
+bool sendToMediaMetrics(const PerformanceData& data);
+
+//------------------------------------------------------------------------------
+
constexpr int kMsPerSec = 1000;
constexpr int kSecPerMin = 60;
constexpr int kJiffyPerMs = 10; // time unit for histogram as a multiple of milliseconds
// stores a histogram: key: observed buffer period (multiple of jiffy). value: count
-using Histogram = std::map<int, int>;
+using Hist = std::map<int, int>;
using msInterval = double;
using jiffyInterval = double;
@@ -54,7 +73,7 @@
}
// Writes outlier intervals, timestamps, peaks timestamps, and histograms to a file.
-void writeToFile(const std::deque<std::pair<timestamp, Histogram>> &hists,
+void writeToFile(const std::deque<std::pair<timestamp, Hist>> &hists,
const std::deque<std::pair<msInterval, timestamp>> &outlierData,
const std::deque<timestamp> &peakTimestamps,
const char * kDirectory, bool append, int author, log_hash_t hash);
diff --git a/media/libstagefright/timedtext/Android.bp b/media/libstagefright/timedtext/Android.bp
index a5ad6c6..7c51333 100644
--- a/media/libstagefright/timedtext/Android.bp
+++ b/media/libstagefright/timedtext/Android.bp
@@ -25,3 +25,36 @@
shared_libs: ["libmedia"],
}
+
+cc_library_static {
+ name: "libstagefright_timedtext2",
+
+ srcs: ["TextDescriptions2.cpp"],
+
+ static_libs: [
+ "libmediaplayer2-protos",
+ "libprotobuf-cpp-lite",
+ ],
+
+ cflags: [
+ "-Wno-multichar",
+ "-Werror",
+ "-Wall",
+ ],
+
+ sanitize: {
+ misc_undefined: [
+ "signed-integer-overflow",
+ ],
+ cfi: true,
+ diag: {
+ cfi: true,
+ },
+ },
+
+ include_dirs: [
+ "frameworks/av/media/libstagefright",
+ ],
+
+ shared_libs: ["libmedia"],
+}
diff --git a/media/libstagefright/timedtext/TextDescriptions2.cpp b/media/libstagefright/timedtext/TextDescriptions2.cpp
new file mode 100644
index 0000000..f48eacc
--- /dev/null
+++ b/media/libstagefright/timedtext/TextDescriptions2.cpp
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TextDescriptions2.h"
+#include <media/stagefright/foundation/ByteUtils.h>
+#include <media/stagefright/MediaErrors.h>
+
+namespace android {
+
+TextDescriptions2::TextDescriptions2() {
+}
+
+status_t TextDescriptions2::getPlayerMessageOfDescriptions(
+ const uint8_t *data, ssize_t size,
+ uint32_t flags, int timeMs, PlayerMessage *playerMsg) {
+ if (flags & IN_BAND_TEXT_3GPP) {
+ if (flags & GLOBAL_DESCRIPTIONS) {
+ return extract3GPPGlobalDescriptions(data, size, playerMsg);
+ } else if (flags & LOCAL_DESCRIPTIONS) {
+ return extract3GPPLocalDescriptions(data, size, timeMs, playerMsg);
+ }
+ } else if (flags & OUT_OF_BAND_TEXT_SRT) {
+ if (flags & LOCAL_DESCRIPTIONS) {
+ return extractSRTLocalDescriptions(data, size, timeMs, playerMsg);
+ }
+ }
+
+ return ERROR_UNSUPPORTED;
+}
+
+// Parse the SRT text sample, and store the timing and text sample in a PlayerMessage.
+// The PlayerMessage will be sent to MediaPlayer2.java through event, and will be
+// parsed in TimedText.java.
+status_t TextDescriptions2::extractSRTLocalDescriptions(
+ const uint8_t *data, ssize_t size, int timeMs, PlayerMessage *playerMsg) {
+ playerMsg->add_values()->set_int32_value(KEY_LOCAL_SETTING);
+ playerMsg->add_values()->set_int32_value(KEY_START_TIME);
+ playerMsg->add_values()->set_int32_value(timeMs);
+
+ playerMsg->add_values()->set_int32_value(KEY_STRUCT_TEXT);
+ playerMsg->add_values()->set_bytes_value(data, size);
+
+ return OK;
+}
+
+// Extract the local 3GPP display descriptions. 3GPP local descriptions
+// are appended to the text sample if any.
+status_t TextDescriptions2::extract3GPPLocalDescriptions(
+ const uint8_t *data, ssize_t size,
+ int timeMs, PlayerMessage *playerMsg) {
+
+ playerMsg->add_values()->set_int32_value(KEY_LOCAL_SETTING);
+
+ // write start time to display this text sample
+ playerMsg->add_values()->set_int32_value(KEY_START_TIME);
+ playerMsg->add_values()->set_int32_value(timeMs);
+
+ if (size < 2) {
+ return OK;
+ }
+ ssize_t textLen = (*data) << 8 | (*(data + 1));
+
+ if (size < textLen + 2) {
+ return OK;
+ }
+
+ // write text sample length and text sample itself
+ playerMsg->add_values()->set_int32_value(KEY_STRUCT_TEXT);
+ playerMsg->add_values()->set_bytes_value(data + 2, textLen);
+
+ if (size > textLen + 2) {
+ data += (textLen + 2);
+ size -= (textLen + 2);
+ } else {
+ return OK;
+ }
+
+ while (size >= 8) {
+ const uint8_t *tmpData = data;
+ ssize_t chunkSize = U32_AT(tmpData); // size includes size and type
+ uint32_t chunkType = U32_AT(tmpData + 4);
+
+ if (chunkSize <= 8 || chunkSize > size) {
+ return OK;
+ }
+
+ size_t remaining = chunkSize - 8;
+
+ tmpData += 8;
+
+ switch(chunkType) {
+ // 'tbox' box to indicate the position of the text with values
+ // of top, left, bottom and right
+ case FOURCC('t', 'b', 'o', 'x'):
+ {
+ if (remaining < 8) {
+ return OK;
+ }
+ playerMsg->add_values()->set_int32_value(KEY_STRUCT_TEXT_POS);
+ playerMsg->add_values()->set_int32_value(U16_AT(tmpData));
+ playerMsg->add_values()->set_int32_value(U16_AT(tmpData + 2));
+ playerMsg->add_values()->set_int32_value(U16_AT(tmpData + 4));
+ playerMsg->add_values()->set_int32_value(U16_AT(tmpData + 6));
+
+ tmpData += 8;
+ remaining -= 8;
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ data += chunkSize;
+ size -= chunkSize;
+ }
+
+ return OK;
+}
+
+// To extract box 'tx3g' defined in 3GPP TS 26.245, and store it in a PlayerMessage
+status_t TextDescriptions2::extract3GPPGlobalDescriptions(
+ const uint8_t *data, ssize_t size, PlayerMessage *playerMsg) {
+
+ playerMsg->add_values()->set_int32_value(KEY_GLOBAL_SETTING);
+
+ while (size >= 8) {
+ ssize_t chunkSize = U32_AT(data);
+ uint32_t chunkType = U32_AT(data + 4);
+ const uint8_t *tmpData = data;
+ tmpData += 8;
+ size_t remaining = size - 8;
+
+ if (size < chunkSize) {
+ return OK;
+ }
+ switch(chunkType) {
+ case FOURCC('t', 'x', '3', 'g'):
+ {
+ if (remaining < 18) {
+ return OK;
+ }
+ // Skip DISPLAY_FLAGS, STRUCT_JUSTIFICATION, and BACKGROUND_COLOR_RGBA
+ tmpData += 18;
+ remaining -= 18;
+
+ if (remaining < 8) {
+ return OK;
+ }
+ playerMsg->add_values()->set_int32_value(KEY_STRUCT_TEXT_POS);
+ playerMsg->add_values()->set_int32_value(U16_AT(tmpData));
+ playerMsg->add_values()->set_int32_value(U16_AT(tmpData + 2));
+ playerMsg->add_values()->set_int32_value(U16_AT(tmpData + 4));
+ playerMsg->add_values()->set_int32_value(U16_AT(tmpData + 6));
+
+ tmpData += 8;
+ remaining -= 18;
+ // Ignore remaining data.
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ data += chunkSize;
+ size -= chunkSize;
+ }
+
+ return OK;
+}
+
+} // namespace android
diff --git a/media/libstagefright/timedtext/TextDescriptions2.h b/media/libstagefright/timedtext/TextDescriptions2.h
new file mode 100644
index 0000000..7c7d2d0
--- /dev/null
+++ b/media/libstagefright/timedtext/TextDescriptions2.h
@@ -0,0 +1,88 @@
+ /*
+ * Copyright (C) 2018 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 TEXT_DESCRIPTIONS2_H_
+
+#define TEXT_DESCRIPTIONS2_H_
+
+#include <binder/Parcel.h>
+#include <media/stagefright/foundation/ABase.h>
+
+#include "mediaplayer2.pb.h"
+
+using android::media::MediaPlayer2Proto::PlayerMessage;
+
+namespace android {
+
+class TextDescriptions2 {
+public:
+ enum {
+ IN_BAND_TEXT_3GPP = 0x01,
+ OUT_OF_BAND_TEXT_SRT = 0x02,
+
+ GLOBAL_DESCRIPTIONS = 0x100,
+ LOCAL_DESCRIPTIONS = 0x200,
+ };
+
+ static status_t getPlayerMessageOfDescriptions(
+ const uint8_t *data, ssize_t size,
+ uint32_t flags, int timeMs, PlayerMessage *playerMsg);
+private:
+ TextDescriptions2();
+
+ enum {
+ // These keys must be in sync with the keys in TimedText.java
+ KEY_DISPLAY_FLAGS = 1, // int
+ KEY_STYLE_FLAGS = 2, // int
+ KEY_BACKGROUND_COLOR_RGBA = 3, // int
+ KEY_HIGHLIGHT_COLOR_RGBA = 4, // int
+ KEY_SCROLL_DELAY = 5, // int
+ KEY_WRAP_TEXT = 6, // int
+ KEY_START_TIME = 7, // int
+ KEY_STRUCT_BLINKING_TEXT_LIST = 8, // List<CharPos>
+ KEY_STRUCT_FONT_LIST = 9, // List<Font>
+ KEY_STRUCT_HIGHLIGHT_LIST = 10, // List<CharPos>
+ KEY_STRUCT_HYPER_TEXT_LIST = 11, // List<HyperText>
+ KEY_STRUCT_KARAOKE_LIST = 12, // List<Karaoke>
+ KEY_STRUCT_STYLE_LIST = 13, // List<Style>
+ KEY_STRUCT_TEXT_POS = 14, // TextPos
+ KEY_STRUCT_JUSTIFICATION = 15, // Justification
+ KEY_STRUCT_TEXT = 16, // Text
+
+ KEY_GLOBAL_SETTING = 101,
+ KEY_LOCAL_SETTING = 102,
+ KEY_START_CHAR = 103,
+ KEY_END_CHAR = 104,
+ KEY_FONT_ID = 105,
+ KEY_FONT_SIZE = 106,
+ KEY_TEXT_COLOR_RGBA = 107,
+ };
+
+ static status_t extractSRTLocalDescriptions(
+ const uint8_t *data, ssize_t size,
+ int timeMs, PlayerMessage *playerMsg);
+ static status_t extract3GPPGlobalDescriptions(
+ const uint8_t *data, ssize_t size,
+ PlayerMessage *playerMsg);
+ static status_t extract3GPPLocalDescriptions(
+ const uint8_t *data, ssize_t size,
+ int timeMs, PlayerMessage *playerMsg);
+
+ DISALLOW_EVIL_CONSTRUCTORS(TextDescriptions2);
+};
+
+} // namespace android
+#endif // TEXT_DESCRIPTIONS2_H_
diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp
index fd784a4..f650b66 100644
--- a/services/audioflinger/FastMixer.cpp
+++ b/services/audioflinger/FastMixer.cpp
@@ -195,6 +195,9 @@
// to avoid blocking here and to prevent possible priority inversion
mMixer = new AudioMixer(frameCount, mSampleRate);
// FIXME See the other FIXME at FastMixer::setNBLogWriter()
+ // TODO define an int to thread type mapping for the "2" below.
+ const NBLog::thread_info_t info = { 2 /*FastMixer*/, frameCount, mSampleRate };
+ LOG_THREAD_INFO(info);
const size_t mixerFrameSize = mSinkChannelCount
* audio_bytes_per_sample(mMixerBufferFormat);
mMixerBufferSize = mixerFrameSize * frameCount;
diff --git a/services/audioflinger/FastThread.cpp b/services/audioflinger/FastThread.cpp
index 6f223df..09101d9 100644
--- a/services/audioflinger/FastThread.cpp
+++ b/services/audioflinger/FastThread.cpp
@@ -22,6 +22,7 @@
#include "Configuration.h"
#include <linux/futex.h>
#include <sys/syscall.h>
+#include <audio_utils/clock.h>
#include <cutils/atomic.h>
#include <utils/Log.h>
#include <utils/Trace.h>
@@ -260,6 +261,9 @@
mIsWarm = true;
mDumpState->mMeasuredWarmupTs = mMeasuredWarmupTs;
mDumpState->mWarmupCycles = mWarmupCycles;
+ const double measuredWarmupMs = (mMeasuredWarmupTs.tv_sec * 1e3) +
+ (mMeasuredWarmupTs.tv_nsec * 1e-6);
+ LOG_WARMUP_TIME(measuredWarmupMs);
}
}
mSleepNs = -1;
@@ -270,6 +274,7 @@
ALOGV("underrun: time since last cycle %d.%03ld sec",
(int) sec, nsec / 1000000L);
mDumpState->mUnderruns++;
+ LOG_UNDERRUN(audio_utils_ns_from_timespec(&newTs));
mIgnoreNextOverrun = true;
} else if (nsec < mOverrunNs) {
if (mIgnoreNextOverrun) {
@@ -279,6 +284,7 @@
ALOGV("overrun: time since last cycle %d.%03ld sec",
(int) sec, nsec / 1000000L);
mDumpState->mOverruns++;
+ LOG_OVERRUN(audio_utils_ns_from_timespec(&newTs));
}
// This forces a minimum cycle time. It:
// - compensates for an audio HAL with jitter due to sample rate conversion
diff --git a/services/audioflinger/TypedLogger.h b/services/audioflinger/TypedLogger.h
index 6677470..dd2e7c9 100644
--- a/services/audioflinger/TypedLogger.h
+++ b/services/audioflinger/TypedLogger.h
@@ -107,6 +107,23 @@
#define LOG_LATENCY(ms) do { NBLog::Writer *x = tlNBLogWriter; if (x != nullptr) \
x->log<NBLog::EVENT_LATENCY>(ms); } while (0)
+// Record thread warmup time in milliseconds. Parameter ms is of type double.
+#define LOG_WARMUP_TIME(ms) do { NBLog::Writer *x = tlNBLogWriter; if (x != nullptr) \
+ x->log<NBLog::EVENT_WARMUP_TIME>(ms); } while (0)
+
+// Record thread underrun event nanosecond timestamp. Parameter ns is an int64_t.
+#define LOG_UNDERRUN(ns) do { NBLog::Writer *x = tlNBLogWriter; if (x != nullptr) \
+ x->log<NBLog::EVENT_UNDERRUN>(ns); } while (0)
+
+// Record thread overrun event nanosecond timestamp. Parameter ns is an int64_t.
+#define LOG_OVERRUN(ns) do { NBLog::Writer *x = tlNBLogWriter; if (x != nullptr) \
+ x->log<NBLog::EVENT_OVERRUN>(ns); } while (0)
+
+// Record thread info. This currently includes type, frameCount, and sampleRate.
+// Parameter type is thread_info_t as defined in NBLog.h.
+#define LOG_THREAD_INFO(info) do { NBLog::Writer *x = tlNBLogWriter; \
+ if (x != nullptr) x->log<NBLog::EVENT_THREAD_INFO>(info); } while (0)
+
namespace android {
extern "C" {
extern thread_local NBLog::Writer *tlNBLogWriter;
diff --git a/services/mediaanalytics/MediaAnalyticsService.cpp b/services/mediaanalytics/MediaAnalyticsService.cpp
index 4b05395..45da56a 100644
--- a/services/mediaanalytics/MediaAnalyticsService.cpp
+++ b/services/mediaanalytics/MediaAnalyticsService.cpp
@@ -483,6 +483,7 @@
{
"audiopolicy",
"audiorecord",
+ "audiothread",
"audiotrack",
"codec",
"extractor",