From 4033e5937f2e4b527cb90ccc99696ad915c10855 Mon Sep 17 00:00:00 2001 From: Johny Lin Date: Mon, 26 Jun 2017 16:21:11 +0800 Subject: [PATCH 0001/1203] codec2: fix public definiton for C2ComponentStore Test: codec2 test Change-Id: I7781f8d5b418c2991bcd0e2758fa7930ee81cedd --- media/libstagefright/codec2/include/C2Component.h | 1 + 1 file changed, 1 insertion(+) diff --git a/media/libstagefright/codec2/include/C2Component.h b/media/libstagefright/codec2/include/C2Component.h index 1ee9302e7c..aeaf829e5e 100644 --- a/media/libstagefright/codec2/include/C2Component.h +++ b/media/libstagefright/codec2/include/C2Component.h @@ -543,6 +543,7 @@ public: }; class C2ComponentStore { +public: /** * Creates a component. * -- GitLab From 89dc2fe68646a16ee21a02ce2b6610bbf3262ade Mon Sep 17 00:00:00 2001 From: Wei Jia Date: Tue, 18 Jul 2017 11:00:40 -0700 Subject: [PATCH 0002/1203] GenericSource: still feed audio decoder when paused. Test: audio decoder stops polling source Bug: 63412809 Change-Id: I53c98920ea850defae88b2e12be6b05d6d90c32c --- media/libmediaplayerservice/nuplayer/GenericSource.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp index d83c40619e..c79b6f760d 100644 --- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp +++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp @@ -847,10 +847,6 @@ sp NuPlayer::GenericSource::doGetFormatMeta(bool audio) const { status_t NuPlayer::GenericSource::dequeueAccessUnit( bool audio, sp *accessUnit) { - if (audio && !mStarted) { - return -EWOULDBLOCK; - } - // If has gone through stop/releaseDrm sequence, we no longer send down any buffer b/c // the codec's crypto object has gone away (b/37960096). // Note: This will be unnecessary when stop() changes behavior and releases codec (b/35248283). -- GitLab From f7005138e0d63c79131923ebcab1f9dd9074878b Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Wed, 19 Jul 2017 11:41:23 -0700 Subject: [PATCH 0003/1203] stagefright: allow secure audio input buffer Bug: 63343701 Test: adb shell am instrument -e size small -w 'android.media.cts/android.support.test.runner.AndroidJUnitRunner' Change-Id: I64cae10d17ec7421ffdc60dd0aa202f6c74f83bf --- media/libstagefright/ACodec.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index 142ae07f8c..961f119c66 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -1861,16 +1861,15 @@ status_t ACodec::configureCodec( mFlags |= kFlagIsGrallocUsageProtected; mFlags |= kFlagPushBlankBuffersToNativeWindowOnShutdown; } + } + if (mFlags & kFlagIsSecure) { + // use native_handles for secure input buffers + err = setPortMode(kPortIndexInput, IOMX::kPortModePresetSecureBuffer); - if (mFlags & kFlagIsSecure) { - // use native_handles for secure input buffers - err = setPortMode(kPortIndexInput, IOMX::kPortModePresetSecureBuffer); - - if (err != OK) { - ALOGI("falling back to non-native_handles"); - setPortMode(kPortIndexInput, IOMX::kPortModePresetByteBuffer); - err = OK; // ignore error for now - } + if (err != OK) { + ALOGI("falling back to non-native_handles"); + setPortMode(kPortIndexInput, IOMX::kPortModePresetByteBuffer); + err = OK; // ignore error for now } } if (haveNativeWindow) { -- GitLab From 8c3d2e89ece54232f23c179d979cc5599f742232 Mon Sep 17 00:00:00 2001 From: Sanna Catherine de Treville Wager Date: Fri, 14 Jul 2017 15:33:49 -0700 Subject: [PATCH 0004/1203] Fixed indent in NBLog.h Test: dumpsys media.log Change-Id: Ic6cf4adc2da4759b165c85d15bd4fd9d3c6780a0 --- media/libnbaio/include/media/nbaio/NBLog.h | 908 +++++++++++---------- 1 file changed, 456 insertions(+), 452 deletions(-) diff --git a/media/libnbaio/include/media/nbaio/NBLog.h b/media/libnbaio/include/media/nbaio/NBLog.h index 3e48ee16d0..3189e4e2b9 100644 --- a/media/libnbaio/include/media/nbaio/NBLog.h +++ b/media/libnbaio/include/media/nbaio/NBLog.h @@ -37,234 +37,236 @@ class NBLog { public: -typedef uint64_t log_hash_t; - -// FIXME Everything needed for client (writer API and registration) should be isolated -// from the rest of the implementation. -class Writer; -class Reader; - -enum Event : uint8_t { - EVENT_RESERVED, - EVENT_STRING, // ASCII string, not NUL-terminated - // TODO: make timestamp optional - EVENT_TIMESTAMP, // clock_gettime(CLOCK_MONOTONIC) - EVENT_INTEGER, // integer value entry - EVENT_FLOAT, // floating point value entry - EVENT_PID, // process ID and process name - EVENT_AUTHOR, // author index (present in merged logs) tracks entry's original log - EVENT_START_FMT, // logFormat start event: entry includes format string, following - // entries contain format arguments - EVENT_HASH, // unique HASH of log origin, originates from hash of file name - // and line number - EVENT_HISTOGRAM_ENTRY_TS, // single datum for timestamp histogram - EVENT_AUDIO_STATE, // audio on/off event: logged upon FastMixer::onStateChange() call - EVENT_END_FMT, // end of logFormat argument list - - EVENT_UPPER_BOUND, // to check for invalid events -}; + typedef uint64_t log_hash_t; + + // FIXME Everything needed for client (writer API and registration) should be isolated + // from the rest of the implementation. + class Writer; + class Reader; + + enum Event : uint8_t { + EVENT_RESERVED, + EVENT_STRING, // ASCII string, not NUL-terminated + // TODO: make timestamp optional + EVENT_TIMESTAMP, // clock_gettime(CLOCK_MONOTONIC) + EVENT_INTEGER, // integer value entry + EVENT_FLOAT, // floating point value entry + EVENT_PID, // process ID and process name + EVENT_AUTHOR, // author index (present in merged logs) tracks entry's + // original log + EVENT_START_FMT, // logFormat start event: entry includes format string, + // following entries contain format arguments + EVENT_HASH, // unique HASH of log origin, originates from hash of file name + // and line number + EVENT_HISTOGRAM_ENTRY_TS, // single datum for timestamp histogram + EVENT_AUDIO_STATE, // audio on/off event: logged on FastMixer::onStateChange call + EVENT_END_FMT, // end of logFormat argument list + + EVENT_UPPER_BOUND, // to check for invalid events + }; private: -// --------------------------------------------------------------------------- -// API for handling format entry operations - -// a formatted entry has the following structure: -// * START_FMT entry, containing the format string -// * TIMESTAMP entry -// * HASH entry -// * author entry of the thread that generated it (optional, present in merged log) -// * format arg1 -// * format arg2 -// * ... -// * END_FMT entry - -// entry representation in memory -struct entry { - const uint8_t type; - const uint8_t length; - const uint8_t data[0]; -}; - -// entry tail representation (after data) -struct ending { - uint8_t length; - uint8_t next[0]; -}; - -// entry iterator -class EntryIterator { -public: - EntryIterator(); - explicit EntryIterator(const uint8_t *entry); - EntryIterator(const EntryIterator &other); - - // dereference underlying entry - const entry& operator*() const; - const entry* operator->() const; - // advance to next entry - EntryIterator& operator++(); // ++i - // back to previous entry - EntryIterator& operator--(); // --i - EntryIterator next() const; - EntryIterator prev() const; - bool operator!=(const EntryIterator &other) const; - int operator-(const EntryIterator &other) const; - - bool hasConsistentLength() const; - void copyTo(std::unique_ptr &dst) const; - void copyData(uint8_t *dst) const; - - template - inline const T& payload() { - return *reinterpret_cast(ptr + offsetof(entry, data)); - } + // --------------------------------------------------------------------------- + // API for handling format entry operations + + // a formatted entry has the following structure: + // * START_FMT entry, containing the format string + // * TIMESTAMP entry + // * HASH entry + // * author entry of the thread that generated it (optional, present in merged log) + // * format arg1 + // * format arg2 + // * ... + // * END_FMT entry + + // entry representation in memory + struct entry { + const uint8_t type; + const uint8_t length; + const uint8_t data[0]; + }; - inline operator const uint8_t*() const { - return ptr; - } + // entry tail representation (after data) + struct ending { + uint8_t length; + uint8_t next[0]; + }; -private: - const uint8_t *ptr; -}; + // entry iterator + class EntryIterator { + public: + EntryIterator(); + explicit EntryIterator(const uint8_t *entry); + EntryIterator(const EntryIterator &other); + + // dereference underlying entry + const entry& operator*() const; + const entry* operator->() const; + // advance to next entry + EntryIterator& operator++(); // ++i + // back to previous entry + EntryIterator& operator--(); // --i + EntryIterator next() const; + EntryIterator prev() const; + bool operator!=(const EntryIterator &other) const; + int operator-(const EntryIterator &other) const; + + bool hasConsistentLength() const; + void copyTo(std::unique_ptr &dst) const; + void copyData(uint8_t *dst) const; + + template + inline const T& payload() { + return *reinterpret_cast(ptr + offsetof(entry, data)); + } + + inline operator const uint8_t*() const { + return ptr; + } -class AbstractEntry { -public: + private: + const uint8_t *ptr; + }; - // Entry starting in the given pointer - explicit AbstractEntry(const uint8_t *entry); - virtual ~AbstractEntry() {} + class AbstractEntry { + public: - // build concrete entry of appropriate class from pointer - static std::unique_ptr buildEntry(const uint8_t *ptr); + // Entry starting in the given pointer + explicit AbstractEntry(const uint8_t *entry); + virtual ~AbstractEntry() {} - // get format entry timestamp - // TODO consider changing to uint64_t - virtual int64_t timestamp() const = 0; + // build concrete entry of appropriate class from pointer + static std::unique_ptr buildEntry(const uint8_t *ptr); - // get format entry's unique id - virtual log_hash_t hash() const = 0; + // get format entry timestamp + // TODO consider changing to uint64_t + virtual int64_t timestamp() const = 0; - // entry's author index (-1 if none present) - // a Merger has a vector of Readers, author simply points to the index of the - // Reader that originated the entry - // TODO consider changing to uint32_t - virtual int author() const = 0; + // get format entry's unique id + virtual log_hash_t hash() const = 0; - // copy entry, adding author before timestamp, returns iterator to end of entry - virtual EntryIterator copyWithAuthor(std::unique_ptr &dst, - int author) const = 0; + // entry's author index (-1 if none present) + // a Merger has a vector of Readers, author simply points to the index of the + // Reader that originated the entry + // TODO consider changing to uint32_t + virtual int author() const = 0; -protected: - // copies ordinary entry from src to dst, and returns length of entry - // size_t copyEntry(audio_utils_fifo_writer *dst, const iterator &it); - const uint8_t *mEntry; -}; + // copy entry, adding author before timestamp, returns iterator to end of entry + virtual EntryIterator copyWithAuthor(std::unique_ptr &dst, + int author) const = 0; -class FormatEntry : public AbstractEntry { -public: - // explicit FormatEntry(const EntryIterator &it); - explicit FormatEntry(const uint8_t *ptr) : AbstractEntry(ptr) {} - virtual ~FormatEntry() {} + protected: + // copies ordinary entry from src to dst, and returns length of entry + // size_t copyEntry(audio_utils_fifo_writer *dst, const iterator &it); + const uint8_t *mEntry; + }; - EntryIterator begin() const; + class FormatEntry : public AbstractEntry { + public: + // explicit FormatEntry(const EntryIterator &it); + explicit FormatEntry(const uint8_t *ptr) : AbstractEntry(ptr) {} + virtual ~FormatEntry() {} - // Entry's format string - const char* formatString() const; + EntryIterator begin() const; - // Enrty's format string length - size_t formatStringLength() const; + // Entry's format string + const char* formatString() const; - // Format arguments (excluding format string, timestamp and author) - EntryIterator args() const; + // Enrty's format string length + size_t formatStringLength() const; - // get format entry timestamp - virtual int64_t timestamp() const override; + // Format arguments (excluding format string, timestamp and author) + EntryIterator args() const; - // get format entry's unique id - virtual log_hash_t hash() const override; + // get format entry timestamp + virtual int64_t timestamp() const override; - // entry's author index (-1 if none present) - // a Merger has a vector of Readers, author simply points to the index of the - // Reader that originated the entry - virtual int author() const override; + // get format entry's unique id + virtual log_hash_t hash() const override; - // copy entry, adding author before timestamp, returns size of original entry - virtual EntryIterator copyWithAuthor(std::unique_ptr &dst, - int author) const override; + // entry's author index (-1 if none present) + // a Merger has a vector of Readers, author simply points to the index of the + // Reader that originated the entry + virtual int author() const override; -}; + // copy entry, adding author before timestamp, returns size of original entry + virtual EntryIterator copyWithAuthor(std::unique_ptr &dst, + int author) const override; -class HistogramEntry : public AbstractEntry { -public: - explicit HistogramEntry(const uint8_t *ptr) : AbstractEntry(ptr) { - } - virtual ~HistogramEntry() {} + }; - virtual int64_t timestamp() const override; + class HistogramEntry : public AbstractEntry { + public: + explicit HistogramEntry(const uint8_t *ptr) : AbstractEntry(ptr) { + } + virtual ~HistogramEntry() {} - virtual log_hash_t hash() const override; + virtual int64_t timestamp() const override; - virtual int author() const override; + virtual log_hash_t hash() const override; - virtual EntryIterator copyWithAuthor(std::unique_ptr &dst, - int author) const override; + virtual int author() const override; -}; + virtual EntryIterator copyWithAuthor(std::unique_ptr &dst, + int author) const override; -// --------------------------------------------------------------------------- + }; -// representation of a single log entry in private memory -struct Entry { - Entry(Event event, const void *data, size_t length) - : mEvent(event), mLength(length), mData(data) { } - /*virtual*/ ~Entry() { } + // --------------------------------------------------------------------------- - // used during writing to format Entry information as follows: [type][length][data ... ][length] - int copyEntryDataAt(size_t offset) const; + // representation of a single log entry in private memory + struct Entry { + Entry(Event event, const void *data, size_t length) + : mEvent(event), mLength(length), mData(data) { } + /*virtual*/ ~Entry() { } -private: - friend class Writer; - Event mEvent; // event type - uint8_t mLength; // length of additional data, 0 <= mLength <= kMaxLength - const void *mData; // event type-specific data - static const size_t kMaxLength = 255; -public: - // mEvent, mLength, mData[...], duplicate mLength - static const size_t kOverhead = sizeof(entry) + sizeof(ending); - // endind length of previous entry - static const size_t kPreviousLengthOffset = - sizeof(ending) + - offsetof(ending, length); -}; - -struct HistTsEntry { - log_hash_t hash; - int64_t ts; -}; //TODO __attribute__((packed)); - -struct HistTsEntryWithAuthor { - log_hash_t hash; - int64_t ts; - int author; -}; //TODO __attribute__((packed)); - -using StateTsEntryWithAuthor = HistTsEntryWithAuthor; - -struct HistIntEntry { - log_hash_t hash; - int value; -}; //TODO __attribute__((packed)); - -// representation of a single log entry in shared memory -// byte[0] mEvent -// byte[1] mLength -// byte[2] mData[0] -// ... -// byte[2+i] mData[i] -// ... -// byte[2+mLength-1] mData[mLength-1] -// byte[2+mLength] duplicate copy of mLength to permit reverse scan -// byte[3+mLength] start of next log entry + // used during writing to format Entry information as follows: + // [type][length][data ... ][length] + int copyEntryDataAt(size_t offset) const; + + private: + friend class Writer; + Event mEvent; // event type + uint8_t mLength; // length of additional data, 0 <= mLength <= kMaxLength + const void *mData; // event type-specific data + static const size_t kMaxLength = 255; + public: + // mEvent, mLength, mData[...], duplicate mLength + static const size_t kOverhead = sizeof(entry) + sizeof(ending); + // endind length of previous entry + static const size_t kPreviousLengthOffset = - sizeof(ending) + + offsetof(ending, length); + }; + + struct HistTsEntry { + log_hash_t hash; + int64_t ts; + }; //TODO __attribute__((packed)); + + struct HistTsEntryWithAuthor { + log_hash_t hash; + int64_t ts; + int author; + }; //TODO __attribute__((packed)); + + using StateTsEntryWithAuthor = HistTsEntryWithAuthor; + + struct HistIntEntry { + log_hash_t hash; + int value; + }; //TODO __attribute__((packed)); + + // representation of a single log entry in shared memory + // byte[0] mEvent + // byte[1] mLength + // byte[2] mData[0] + // ... + // byte[2+i] mData[i] + // ... + // byte[2+mLength-1] mData[mLength-1] + // byte[2+mLength] duplicate copy of mLength to permit reverse scan + // byte[3+mLength] start of next log entry static void appendInt(String8 *body, const void *data); static void appendFloat(String8 *body, const void *data); @@ -275,309 +277,311 @@ struct HistIntEntry { static String8 bufferDump(const EntryIterator &it); public: -// Located in shared memory, must be POD. -// Exactly one process must explicitly call the constructor or use placement new. -// Since this is a POD, the destructor is empty and unnecessary to call it explicitly. -struct Shared { - Shared() /* mRear initialized via default constructor */ { } - /*virtual*/ ~Shared() { } + // Located in shared memory, must be POD. + // Exactly one process must explicitly call the constructor or use placement new. + // Since this is a POD, the destructor is empty and unnecessary to call it explicitly. + struct Shared { + Shared() /* mRear initialized via default constructor */ { } + /*virtual*/ ~Shared() { } - audio_utils_fifo_index mRear; // index one byte past the end of most recent Entry - char mBuffer[0]; // circular buffer for entries -}; + audio_utils_fifo_index mRear; // index one byte past the end of most recent Entry + char mBuffer[0]; // circular buffer for entries + }; public: -// --------------------------------------------------------------------------- + // --------------------------------------------------------------------------- -// FIXME Timeline was intended to wrap Writer and Reader, but isn't actually used yet. -// For now it is just a namespace for sharedSize(). -class Timeline : public RefBase { -public: + // FIXME Timeline was intended to wrap Writer and Reader, but isn't actually used yet. + // For now it is just a namespace for sharedSize(). + class Timeline : public RefBase { + public: #if 0 - Timeline(size_t size, void *shared = NULL); - virtual ~Timeline(); + Timeline(size_t size, void *shared = NULL); + virtual ~Timeline(); #endif - // Input parameter 'size' is the desired size of the timeline in byte units. - // Returns the size rounded up to a power-of-2, plus the constant size overhead for indices. - static size_t sharedSize(size_t size); + // Input parameter 'size' is the desired size of the timeline in byte units. + // Returns the size rounded up to a power-of-2, plus the constant size overhead for indices. + static size_t sharedSize(size_t size); #if 0 -private: - friend class Writer; - friend class Reader; + private: + friend class Writer; + friend class Reader; - const size_t mSize; // circular buffer size in bytes, must be a power of 2 - bool mOwn; // whether I own the memory at mShared - Shared* const mShared; // pointer to shared memory + const size_t mSize; // circular buffer size in bytes, must be a power of 2 + bool mOwn; // whether I own the memory at mShared + Shared* const mShared; // pointer to shared memory #endif -}; + }; -// --------------------------------------------------------------------------- + // --------------------------------------------------------------------------- -// Writer is thread-safe with respect to Reader, but not with respect to multiple threads -// calling Writer methods. If you need multi-thread safety for writing, use LockedWriter. -class Writer : public RefBase { -public: - Writer(); // dummy nop implementation without shared memory - - // Input parameter 'size' is the desired size of the timeline in byte units. - // The size of the shared memory must be at least Timeline::sharedSize(size). - Writer(void *shared, size_t size); - Writer(const sp& iMemory, size_t size); - - virtual ~Writer(); - - // FIXME needs comments, and some should be private - virtual void log(const char *string); - virtual void logf(const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); - virtual void logvf(const char *fmt, va_list ap); - virtual void logTimestamp(); - virtual void logTimestamp(const int64_t ts); - virtual void logInteger(const int x); - virtual void logFloat(const float x); - virtual void logPID(); - virtual void logFormat(const char *fmt, log_hash_t hash, ...); - virtual void logVFormat(const char *fmt, log_hash_t hash, va_list ap); - virtual void logStart(const char *fmt); - virtual void logEnd(); - virtual void logHash(log_hash_t hash); - virtual void logEventHistTs(Event event, log_hash_t hash); - - virtual bool isEnabled() const; - - // return value for all of these is the previous isEnabled() - virtual bool setEnabled(bool enabled); // but won't enable if no shared memory - bool enable() { return setEnabled(true); } - bool disable() { return setEnabled(false); } - - sp getIMemory() const { return mIMemory; } + // Writer is thread-safe with respect to Reader, but not with respect to multiple threads + // calling Writer methods. If you need multi-thread safety for writing, use LockedWriter. + class Writer : public RefBase { + public: + Writer(); // dummy nop implementation without shared memory + + // Input parameter 'size' is the desired size of the timeline in byte units. + // The size of the shared memory must be at least Timeline::sharedSize(size). + Writer(void *shared, size_t size); + Writer(const sp& iMemory, size_t size); + + virtual ~Writer(); + + // FIXME needs comments, and some should be private + virtual void log(const char *string); + virtual void logf(const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); + virtual void logvf(const char *fmt, va_list ap); + virtual void logTimestamp(); + virtual void logTimestamp(const int64_t ts); + virtual void logInteger(const int x); + virtual void logFloat(const float x); + virtual void logPID(); + virtual void logFormat(const char *fmt, log_hash_t hash, ...); + virtual void logVFormat(const char *fmt, log_hash_t hash, va_list ap); + virtual void logStart(const char *fmt); + virtual void logEnd(); + virtual void logHash(log_hash_t hash); + virtual void logEventHistTs(Event event, log_hash_t hash); + + virtual bool isEnabled() const; + + // return value for all of these is the previous isEnabled() + virtual bool setEnabled(bool enabled); // but won't enable if no shared memory + bool enable() { return setEnabled(true); } + bool disable() { return setEnabled(false); } + + sp getIMemory() const { return mIMemory; } -private: - // 0 <= length <= kMaxLength - // writes a single Entry to the FIFO - void log(Event event, const void *data, size_t length); - // checks validity of an event before calling log above this one - void log(const Entry *entry, bool trusted = false); - - Shared* const mShared; // raw pointer to shared memory - sp mIMemory; // ref-counted version, initialized in constructor and then const - audio_utils_fifo * const mFifo; // FIFO itself, - // non-NULL unless constructor fails - audio_utils_fifo_writer * const mFifoWriter; // used to write to FIFO, - // non-NULL unless dummy constructor used - bool mEnabled; // whether to actually log - - // cached pid and process name to use in %p format specifier - // total tag length is mPidTagSize and process name is not zero terminated - char *mPidTag; - size_t mPidTagSize; -}; - -// --------------------------------------------------------------------------- - -// Similar to Writer, but safe for multiple threads to call concurrently -class LockedWriter : public Writer { -public: - LockedWriter(); - LockedWriter(void *shared, size_t size); - - virtual void log(const char *string); - virtual void logf(const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); - virtual void logvf(const char *fmt, va_list ap); - virtual void logTimestamp(); - virtual void logTimestamp(const int64_t ts); - virtual void logInteger(const int x); - virtual void logFloat(const float x); - virtual void logPID(); - virtual void logStart(const char *fmt); - virtual void logEnd(); - virtual void logHash(log_hash_t hash); - - virtual bool isEnabled() const; - virtual bool setEnabled(bool enabled); + private: + // 0 <= length <= kMaxLength + // writes a single Entry to the FIFO + void log(Event event, const void *data, size_t length); + // checks validity of an event before calling log above this one + void log(const Entry *entry, bool trusted = false); + + Shared* const mShared; // raw pointer to shared memory + sp mIMemory; // ref-counted version, initialized in constructor + // and then const + audio_utils_fifo * const mFifo; // FIFO itself, non-NULL + // unless constructor fails + audio_utils_fifo_writer * const mFifoWriter; // used to write to FIFO, non-NULL + // unless dummy constructor used + bool mEnabled; // whether to actually log + + // cached pid and process name to use in %p format specifier + // total tag length is mPidTagSize and process name is not zero terminated + char *mPidTag; + size_t mPidTagSize; + }; -private: - mutable Mutex mLock; -}; + // --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- + // Similar to Writer, but safe for multiple threads to call concurrently + class LockedWriter : public Writer { + public: + LockedWriter(); + LockedWriter(void *shared, size_t size); + + virtual void log(const char *string); + virtual void logf(const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); + virtual void logvf(const char *fmt, va_list ap); + virtual void logTimestamp(); + virtual void logTimestamp(const int64_t ts); + virtual void logInteger(const int x); + virtual void logFloat(const float x); + virtual void logPID(); + virtual void logStart(const char *fmt); + virtual void logEnd(); + virtual void logHash(log_hash_t hash); + + virtual bool isEnabled() const; + virtual bool setEnabled(bool enabled); -class Reader : public RefBase { -public: + private: + mutable Mutex mLock; + }; + + // --------------------------------------------------------------------------- - // A snapshot of a readers buffer - // This is raw data. No analysis has been done on it - class Snapshot { + class Reader : public RefBase { public: - Snapshot() : mData(NULL), mLost(0) {} - Snapshot(size_t bufferSize) : mData(new uint8_t[bufferSize]) {} + // A snapshot of a readers buffer + // This is raw data. No analysis has been done on it + class Snapshot { + public: + Snapshot() : mData(NULL), mLost(0) {} - ~Snapshot() { delete[] mData; } + Snapshot(size_t bufferSize) : mData(new uint8_t[bufferSize]) {} - // copy of the buffer - uint8_t *data() const { return mData; } + ~Snapshot() { delete[] mData; } - // amount of data lost (given by audio_utils_fifo_reader) - size_t lost() const { return mLost; } + // copy of the buffer + uint8_t *data() const { return mData; } - // iterator to beginning of readable segment of snapshot - // data between begin and end has valid entries - EntryIterator begin() { return mBegin; } + // amount of data lost (given by audio_utils_fifo_reader) + size_t lost() const { return mLost; } - // iterator to end of readable segment of snapshot - EntryIterator end() { return mEnd; } + // iterator to beginning of readable segment of snapshot + // data between begin and end has valid entries + EntryIterator begin() { return mBegin; } - private: - friend class Reader; - uint8_t *mData; - size_t mLost; - EntryIterator mBegin; - EntryIterator mEnd; - }; + // iterator to end of readable segment of snapshot + EntryIterator end() { return mEnd; } - // Input parameter 'size' is the desired size of the timeline in byte units. - // The size of the shared memory must be at least Timeline::sharedSize(size). - Reader(const void *shared, size_t size); - Reader(const sp& iMemory, size_t size); + private: + friend class Reader; + uint8_t *mData; + size_t mLost; + EntryIterator mBegin; + EntryIterator mEnd; + }; - virtual ~Reader(); + // Input parameter 'size' is the desired size of the timeline in byte units. + // The size of the shared memory must be at least Timeline::sharedSize(size). + Reader(const void *shared, size_t size); + Reader(const sp& iMemory, size_t size); - // get snapshot of readers fifo buffer, effectively consuming the buffer - std::unique_ptr getSnapshot(); - // dump a particular snapshot of the reader - // TODO: move dump to PerformanceAnalysis. Model/view/controller design - void dump(int fd, size_t indent, Snapshot & snap); - // dump the current content of the reader's buffer (call getSnapshot() and previous dump()) - void dump(int fd, size_t indent = 0); - bool isIMemory(const sp& iMemory) const; + virtual ~Reader(); -private: + // get snapshot of readers fifo buffer, effectively consuming the buffer + std::unique_ptr getSnapshot(); + // dump a particular snapshot of the reader + // TODO: move dump to PerformanceAnalysis. Model/view/controller design + void dump(int fd, size_t indent, Snapshot & snap); + // dump the current content of the reader's buffer (call getSnapshot() and previous dump()) + void dump(int fd, size_t indent = 0); + bool isIMemory(const sp& iMemory) const; - static const std::set startingTypes; - static const std::set endingTypes; - /*const*/ Shared* const mShared; // raw pointer to shared memory, actually const but not - // declared as const because audio_utils_fifo() constructor - sp mIMemory; // ref-counted version, assigned only in constructor - int mFd; // file descriptor - int mIndent; // indentation level - audio_utils_fifo * const mFifo; // FIFO itself, - // non-NULL unless constructor fails - audio_utils_fifo_reader * const mFifoReader; // used to read from FIFO, - // non-NULL unless constructor fails - - // TODO: it might be clearer, instead of a direct map from source location to vector of - // timestamps, if we instead first mapped from source location to an object that - // represented that location. And one_of its fields would be a vector of timestamps. - // That would allow us to record other information about the source location beyond timestamps. - void dumpLine(const String8& timestamp, String8& body); - - EntryIterator handleFormat(const FormatEntry &fmtEntry, - String8 *timestamp, - String8 *body); - // dummy method for handling absent author entry - virtual void handleAuthor(const AbstractEntry& /*fmtEntry*/, String8* /*body*/) {} - - // Searches for the last entry of type in the range [front, back) - // back has to be entry-aligned. Returns nullptr if none enconuntered. - static const uint8_t *findLastEntryOfTypes(const uint8_t *front, const uint8_t *back, - const std::set &types); - - static const size_t kSquashTimestamp = 5; // squash this many or more adjacent timestamps -}; - -// Wrapper for a reader with a name. Contains a pointer to the reader and a pointer to the name -class NamedReader { -public: - NamedReader() { mName[0] = '\0'; } // for Vector - NamedReader(const sp& reader, const char *name) : - mReader(reader) - { strlcpy(mName, name, sizeof(mName)); } - ~NamedReader() { } - const sp& reader() const { return mReader; } - const char* name() const { return mName; } + private: -private: - sp mReader; - static const size_t kMaxName = 32; - char mName[kMaxName]; -}; + static const std::set startingTypes; + static const std::set endingTypes; + /*const*/ Shared* const mShared; // raw pointer to shared memory, actually const but not + // declared as const because audio_utils_fifo() constructor + sp mIMemory; // ref-counted version, assigned only in constructor + int mFd; // file descriptor + int mIndent; // indentation level + audio_utils_fifo * const mFifo; // FIFO itself, + // non-NULL unless constructor fails + audio_utils_fifo_reader * const mFifoReader; // used to read from FIFO, + // non-NULL unless constructor fails + + // TODO: it might be clearer, instead of a direct map from source location to vector of + // timestamps, if we instead first mapped from source location to an object that + // represented that location. And one_of its fields would be a vector of timestamps. + // That would allow us to record other information about the source location beyond + // timestamps. + void dumpLine(const String8& timestamp, String8& body); + + EntryIterator handleFormat(const FormatEntry &fmtEntry, + String8 *timestamp, + String8 *body); + // dummy method for handling absent author entry + virtual void handleAuthor(const AbstractEntry& /*fmtEntry*/, String8* /*body*/) {} + + // Searches for the last entry of type in the range [front, back) + // back has to be entry-aligned. Returns nullptr if none enconuntered. + static const uint8_t *findLastEntryOfTypes(const uint8_t *front, const uint8_t *back, + const std::set &types); + + static const size_t kSquashTimestamp = 5; // squash this many or more adjacent timestamps + }; -// --------------------------------------------------------------------------- + // Wrapper for a reader with a name. Contains a pointer to the reader and a pointer to the name + class NamedReader { + public: + NamedReader() { mName[0] = '\0'; } // for Vector + NamedReader(const sp& reader, const char *name) : + mReader(reader) + { strlcpy(mName, name, sizeof(mName)); } + ~NamedReader() { } + const sp& reader() const { return mReader; } + const char* name() const { return mName; } -class Merger : public RefBase { -public: - Merger(const void *shared, size_t size); + private: + sp mReader; + static const size_t kMaxName = 32; + char mName[kMaxName]; + }; - virtual ~Merger() {} + // --------------------------------------------------------------------------- - void addReader(const NamedReader &reader); - // TODO add removeReader - void merge(); - // FIXME This is returning a reference to a shared variable that needs a lock - const std::vector& getNamedReaders() const; -private: - // vector of the readers the merger is supposed to merge from. - // every reader reads from a writer's buffer - // FIXME Needs to be protected by a lock - std::vector mNamedReaders; - - // TODO Need comments on all of these - Shared * const mShared; - std::unique_ptr mFifo; - std::unique_ptr mFifoWriter; -}; - -class MergeReader : public Reader { -public: - MergeReader(const void *shared, size_t size, Merger &merger); -private: - // FIXME Needs to be protected by a lock, - // because even though our use of it is read-only there may be asynchronous updates - const std::vector& mNamedReaders; - // handle author entry by looking up the author's name and appending it to the body - // returns number of bytes read from fmtEntry - void handleAuthor(const AbstractEntry &fmtEntry, String8 *body); -}; - -// MergeThread is a thread that contains a Merger. It works as a retriggerable one-shot: -// when triggered, it awakes for a lapse of time, during which it periodically merges; if -// retriggered, the timeout is reset. -// The thread is triggered on AudioFlinger binder activity. -class MergeThread : public Thread { -public: - MergeThread(Merger &merger); - virtual ~MergeThread() override; + class Merger : public RefBase { + public: + Merger(const void *shared, size_t size); - // Reset timeout and activate thread to merge periodically if it's idle - void wakeup(); + virtual ~Merger() {} - // Set timeout period until the merging thread goes idle again - void setTimeoutUs(int time); + void addReader(const NamedReader &reader); + // TODO add removeReader + void merge(); + // FIXME This is returning a reference to a shared variable that needs a lock + const std::vector& getNamedReaders() const; + private: + // vector of the readers the merger is supposed to merge from. + // every reader reads from a writer's buffer + // FIXME Needs to be protected by a lock + std::vector mNamedReaders; + + // TODO Need comments on all of these + Shared * const mShared; + std::unique_ptr mFifo; + std::unique_ptr mFifoWriter; + }; -private: - virtual bool threadLoop() override; + class MergeReader : public Reader { + public: + MergeReader(const void *shared, size_t size, Merger &merger); + private: + // FIXME Needs to be protected by a lock, + // because even though our use of it is read-only there may be asynchronous updates + const std::vector& mNamedReaders; + // handle author entry by looking up the author's name and appending it to the body + // returns number of bytes read from fmtEntry + void handleAuthor(const AbstractEntry &fmtEntry, String8 *body); + }; - // the merger who actually does the work of merging the logs - Merger& mMerger; + // MergeThread is a thread that contains a Merger. It works as a retriggerable one-shot: + // when triggered, it awakes for a lapse of time, during which it periodically merges; if + // retriggered, the timeout is reset. + // The thread is triggered on AudioFlinger binder activity. + class MergeThread : public Thread { + public: + MergeThread(Merger &merger); + virtual ~MergeThread() override; - // mutex for the condition variable - Mutex mMutex; + // Reset timeout and activate thread to merge periodically if it's idle + void wakeup(); - // condition variable to activate merging on timeout >= 0 - Condition mCond; + // Set timeout period until the merging thread goes idle again + void setTimeoutUs(int time); + + private: + virtual bool threadLoop() override; - // time left until the thread blocks again (in microseconds) - int mTimeoutUs; + // the merger who actually does the work of merging the logs + Merger& mMerger; - // merging period when the thread is awake - static const int kThreadSleepPeriodUs = 1000000 /*1s*/; + // mutex for the condition variable + Mutex mMutex; - // initial timeout value when triggered - static const int kThreadWakeupPeriodUs = 3000000 /*3s*/; -}; + // condition variable to activate merging on timeout >= 0 + Condition mCond; + + // time left until the thread blocks again (in microseconds) + int mTimeoutUs; + + // merging period when the thread is awake + static const int kThreadSleepPeriodUs = 1000000 /*1s*/; + + // initial timeout value when triggered + static const int kThreadWakeupPeriodUs = 3000000 /*3s*/; + }; }; // class NBLog -- GitLab From e48652674ebd4047363a79d38a6018978087223e Mon Sep 17 00:00:00 2001 From: Sanna Catherine de Treville Wager Date: Fri, 14 Jul 2017 16:24:15 -0700 Subject: [PATCH 0005/1203] Separate merge from dump and call merge periodically Every time merge is called by the thread loop, the data is also processed and written to PerformanceAnalysis. A call to dump retrieves the FIFO of processed data in PerformanceAnalysis instead of the unprocessed Reader FIFO, and prints the result to the console. Test: dumpsys media.log Change-Id: Ic479f48e9e4fdf4523a8f15db514dbdd85b70434 --- include/media/nbaio/ReportPerformance.h | 1 + media/libnbaio/NBLog.cpp | 80 +++++++++++++------ media/libnbaio/PerformanceAnalysis.cpp | 2 +- media/libnbaio/ReportPerformance.cpp | 5 +- media/libnbaio/include/media/nbaio/NBLog.h | 79 +++++++++++------- .../include/media/nbaio/PerformanceAnalysis.h | 6 +- .../include/media/nbaio/ReportPerformance.h | 8 +- services/medialog/MediaLogService.cpp | 15 ++-- 8 files changed, 127 insertions(+), 69 deletions(-) create mode 120000 include/media/nbaio/ReportPerformance.h diff --git a/include/media/nbaio/ReportPerformance.h b/include/media/nbaio/ReportPerformance.h new file mode 120000 index 0000000000..bd596e37cb --- /dev/null +++ b/include/media/nbaio/ReportPerformance.h @@ -0,0 +1 @@ +../../../media/libnbaio/include/media/nbaio/ReportPerformance.h \ No newline at end of file diff --git a/media/libnbaio/NBLog.cpp b/media/libnbaio/NBLog.cpp index 2f639d22c2..f00d86fec9 100644 --- a/media/libnbaio/NBLog.cpp +++ b/media/libnbaio/NBLog.cpp @@ -87,7 +87,6 @@ */ #define LOG_TAG "NBLog" -// #define LOG_NDEBUG 0 #include #include @@ -109,7 +108,7 @@ #include #include #include -// #include // used to print callstack +#include #include #include @@ -766,8 +765,8 @@ const std::set NBLog::Reader::endingTypes {NBLog::Event::EVENT_E NBLog::Event::EVENT_AUDIO_STATE}; NBLog::Reader::Reader(const void *shared, size_t size) - : mShared((/*const*/ Shared *) shared), /*mIMemory*/ - mFd(-1), mIndent(0), + : mFd(-1), mIndent(0), mLost(0), + mShared((/*const*/ Shared *) shared), /*mIMemory*/ mFifo(mShared != NULL ? new audio_utils_fifo(size, sizeof(uint8_t), mShared->mBuffer, mShared->mRear, NULL /*throttlesFront*/) : NULL), @@ -805,6 +804,9 @@ const uint8_t *NBLog::Reader::findLastEntryOfTypes(const uint8_t *front, const u return nullptr; // no entry found } +// Copies content of a Reader FIFO into its Snapshot +// The Snapshot has the same raw data, but represented as a sequence of entries +// and an EntryIterator making it possible to process the data. std::unique_ptr NBLog::Reader::getSnapshot() { if (mFifoReader == NULL) { @@ -870,23 +872,19 @@ std::unique_ptr NBLog::Reader::getSnapshot() } -// TODO: move this to PerformanceAnalysis -// TODO: make call to dump periodic so that data in shared FIFO does not get overwritten -void NBLog::Reader::dump(int fd, size_t indent, NBLog::Reader::Snapshot &snapshot) +// 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::Reader::Snapshot &snapshot) { - mFd = fd; - mIndent = indent; String8 timestamp, body; + // TODO: check: is the FIXME below still a problem? // FIXME: this is not thread safe - // TODO: need a separate instance of performanceAnalysis for each thread - // used to store data and to call analysis functions - static ReportPerformance::PerformanceAnalysis performanceAnalysis; + // TODO: add lost data information and notification to ReportPerformance size_t lost = snapshot.lost() + (snapshot.begin() - EntryIterator(snapshot.data())); if (lost > 0) { - body.appendFormat("warning: lost %zu bytes worth of events", lost); - // TODO timestamp empty here, only other choice to wait for the first timestamp event in the - // log to push it out. Consider keeping the timestamp/body between calls to copyEntryDataAt(). - dumpLine(timestamp, body); + // TODO: ultimately, this will be += and reset to 0. TODO: check that this is + // obsolete now that Merger::merge is called periodically. No data should be lost + mLost = lost; } for (auto entry = snapshot.begin(); entry != snapshot.end();) { @@ -902,12 +900,20 @@ void NBLog::Reader::dump(int fd, size_t indent, NBLog::Reader::Snapshot &snapsho memcpy(&hash, &(data->hash), sizeof(hash)); int64_t ts; memcpy(&ts, &data->ts, sizeof(ts)); - performanceAnalysis.logTsEntry(ts); + mThreadPerformanceAnalysis[data->author].logTsEntry(ts); ++entry; break; } case EVENT_AUDIO_STATE: { - performanceAnalysis.handleStateChange(); + HistTsEntryWithAuthor *data = (HistTsEntryWithAuthor *) (entry->data); + // TODO This memcpies are here to avoid unaligned memory access crash. + // There's probably a more efficient way to do it + // TODO: incorporate hash information in mThreadPerformanceAnalysis + // log_hash_t hash; + // memcpy(&hash, &(data->hash), sizeof(hash)); + // int64_t ts; + // memcpy(&ts, &data->ts, sizeof(ts)); + mThreadPerformanceAnalysis[data->author].handleStateChange(); ++entry; break; } @@ -922,19 +928,38 @@ void NBLog::Reader::dump(int fd, size_t indent, NBLog::Reader::Snapshot &snapsho break; } } - performanceAnalysis.reportPerformance(&body); + // FIXME: decide whether to print the warnings here or elsewhere if (!body.isEmpty()) { dumpLine(timestamp, body); } } -void NBLog::Reader::dump(int fd, size_t indent) +void NBLog::MergeReader::getAndProcessSnapshot() { - // get a snapshot, dump it + // get a snapshot, process it std::unique_ptr snap = getSnapshot(); - dump(fd, indent, *snap); + getAndProcessSnapshot(*snap); } +// TODO: move this function to a different class than NBLog::Reader +// writes summary of performance to the console +void NBLog::MergeReader::dump(int fd, size_t indent) +{ + mFd = fd; + mIndent = indent; + String8 body, timestamp; + // TODO: check: is the FIXME below still a problem? + // FIXME: this is not thread safe + for (auto & threadReport : mThreadPerformanceAnalysis) { + threadReport.second.reportPerformance(&body); + } + if (!body.isEmpty()) { + ALOGD("body is not empty"); + dumpLine(timestamp, body); + } +} + +// Writes a string to the console void NBLog::Reader::dumpLine(const String8 ×tamp, String8 &body) { if (mFd >= 0) { @@ -1093,6 +1118,7 @@ NBLog::Merger::Merger(const void *shared, size_t size): {} void NBLog::Merger::addReader(const NBLog::NamedReader &reader) { + // FIXME This is called by binder thread in MediaLogService::registerWriter // but the access to shared variable mNamedReaders is not yet protected by a lock. mNamedReaders.push_back(reader); @@ -1116,7 +1142,7 @@ bool operator>(const struct MergeItem &i1, const struct MergeItem &i2) { return i1.ts > i2.ts || (i1.ts == i2.ts && i1.index > i2.index); } -// Merge registered readers, sorted by timestamp +// Merge registered readers, sorted by timestamp, and write data to a single FIFO in local memory void NBLog::Merger::merge() { // FIXME This is called by merge thread // but the access to shared variable mNamedReaders is not yet protected by a lock. @@ -1173,8 +1199,9 @@ void NBLog::MergeReader::handleAuthor(const NBLog::AbstractEntry &entry, String8 // --------------------------------------------------------------------------- -NBLog::MergeThread::MergeThread(NBLog::Merger &merger) +NBLog::MergeThread::MergeThread(NBLog::Merger &merger, NBLog::MergeReader &mergeReader) : mMerger(merger), + mMergeReader(mergeReader), mTimeoutUs(0) {} NBLog::MergeThread::~MergeThread() { @@ -1196,7 +1223,12 @@ bool NBLog::MergeThread::threadLoop() { mTimeoutUs -= kThreadSleepPeriodUs; } if (doMerge) { + // Merge data from all the readers mMerger.merge(); + // Process the data collected by mMerger and write it to PerformanceAnalysis + // FIXME: decide whether to call getAndProcessSnapshot every time + // or whether to have a separate thread that calls it with a lower frequency + mMergeReader.getAndProcessSnapshot(); } return true; } diff --git a/media/libnbaio/PerformanceAnalysis.cpp b/media/libnbaio/PerformanceAnalysis.cpp index fb3bddc3f6..d44dd45f98 100644 --- a/media/libnbaio/PerformanceAnalysis.cpp +++ b/media/libnbaio/PerformanceAnalysis.cpp @@ -271,7 +271,6 @@ void PerformanceAnalysis::reportPerformance(String8 *body, int maxHeight) { ALOGD("reportPerformance: mRecentHists is empty"); return; } - ALOGD("reportPerformance: hists size %d", static_cast(mRecentHists.size())); // TODO: more elaborate data analysis std::map buckets; for (const auto &shortHist: mRecentHists) { @@ -306,6 +305,7 @@ void PerformanceAnalysis::reportPerformance(String8 *body, int maxHeight) { scalingFactor = (height + maxHeight) / maxHeight; height /= scalingFactor; } + // TODO: print reader (author) ID body->appendFormat("\n%*s", leftPadding + 11, "Occurrences"); // write histogram label line with bucket values body->appendFormat("\n%s", " "); diff --git a/media/libnbaio/ReportPerformance.cpp b/media/libnbaio/ReportPerformance.cpp index dc50ada09c..7d3869ce4a 100644 --- a/media/libnbaio/ReportPerformance.cpp +++ b/media/libnbaio/ReportPerformance.cpp @@ -38,8 +38,8 @@ namespace ReportPerformance { // Writes outlier intervals, timestamps, and histograms spanning long time intervals to a file. // TODO: format the data efficiently and write different types of data to different files -void writeToFile(std::deque> &outlierData, - std::deque> &hists, +void writeToFile(const std::deque> &outlierData, + const std::deque> &hists, const char * kName, bool append) { ALOGD("writing performance data to file"); @@ -65,6 +65,7 @@ void writeToFile(std::deque> &outlierData, for (const auto &bucket : hist.second) { ofs << bucket.first << ": " << bucket.second << "\n"; } + ofs << "\n"; // separate histograms with a newline } ofs.close(); } diff --git a/media/libnbaio/include/media/nbaio/NBLog.h b/media/libnbaio/include/media/nbaio/NBLog.h index 3189e4e2b9..1504f81389 100644 --- a/media/libnbaio/include/media/nbaio/NBLog.h +++ b/media/libnbaio/include/media/nbaio/NBLog.h @@ -19,16 +19,18 @@ #ifndef ANDROID_MEDIA_NBLOG_H #define ANDROID_MEDIA_NBLOG_H -#include -#include -#include -#include - -#include #include +#include #include #include +#include +#include +#include +#include +#include +#include + namespace android { class String8; @@ -409,7 +411,6 @@ public: class Reader : public RefBase { public: - // A snapshot of a readers buffer // This is raw data. No analysis has been done on it class Snapshot { @@ -434,6 +435,7 @@ public: EntryIterator end() { return mEnd; } private: + friend class MergeReader; friend class Reader; uint8_t *mData; size_t mLost; @@ -450,22 +452,26 @@ public: // get snapshot of readers fifo buffer, effectively consuming the buffer std::unique_ptr getSnapshot(); - // dump a particular snapshot of the reader - // TODO: move dump to PerformanceAnalysis. Model/view/controller design - void dump(int fd, size_t indent, Snapshot & snap); - // dump the current content of the reader's buffer (call getSnapshot() and previous dump()) - void dump(int fd, size_t indent = 0); + // print a summary of the performance to the console bool isIMemory(const sp& iMemory) const; - private: + protected: + void dumpLine(const String8& timestamp, String8& body); + EntryIterator handleFormat(const FormatEntry &fmtEntry, + String8 *timestamp, + String8 *body); + int mFd; // file descriptor + int mIndent; // indentation level + int mLost; // bytes of data lost before buffer was read + private: static const std::set startingTypes; static const std::set endingTypes; - /*const*/ Shared* const mShared; // raw pointer to shared memory, actually const but not + // declared as const because audio_utils_fifo() constructor sp mIMemory; // ref-counted version, assigned only in constructor - int mFd; // file descriptor - int mIndent; // indentation level + + /*const*/ Shared* const mShared; // raw pointer to shared memory, actually const but not audio_utils_fifo * const mFifo; // FIFO itself, // non-NULL unless constructor fails audio_utils_fifo_reader * const mFifoReader; // used to read from FIFO, @@ -476,20 +482,14 @@ public: // represented that location. And one_of its fields would be a vector of timestamps. // That would allow us to record other information about the source location beyond // timestamps. - void dumpLine(const String8& timestamp, String8& body); - - EntryIterator handleFormat(const FormatEntry &fmtEntry, - String8 *timestamp, - String8 *body); - // dummy method for handling absent author entry - virtual void handleAuthor(const AbstractEntry& /*fmtEntry*/, String8* /*body*/) {} // Searches for the last entry of type in the range [front, back) // back has to be entry-aligned. Returns nullptr if none enconuntered. static const uint8_t *findLastEntryOfTypes(const uint8_t *front, const uint8_t *back, const std::set &types); - static const size_t kSquashTimestamp = 5; // squash this many or more adjacent timestamps + // dummy method for handling absent author entry + virtual void handleAuthor(const AbstractEntry& /*fmtEntry*/, String8* /*body*/) {} }; // Wrapper for a reader with a name. Contains a pointer to the reader and a pointer to the name @@ -511,6 +511,8 @@ public: // --------------------------------------------------------------------------- + // 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. class Merger : public RefBase { public: Merger(const void *shared, size_t size); @@ -520,27 +522,43 @@ public: void addReader(const NamedReader &reader); // TODO add removeReader void merge(); + // FIXME This is returning a reference to a shared variable that needs a lock const std::vector& getNamedReaders() const; + private: // vector of the readers the merger is supposed to merge from. // every reader reads from a writer's buffer // FIXME Needs to be protected by a lock std::vector mNamedReaders; - // TODO Need comments on all of these - Shared * const mShared; - std::unique_ptr mFifo; - std::unique_ptr mFifoWriter; + Shared * const mShared; // raw pointer to shared memory + std::unique_ptr mFifo; // FIFO itself + std::unique_ptr mFifoWriter; // used to write to FIFO }; + // This class has a pointer to the FIFO in local memory which stores the merged + // data collected by NBLog::Merger from all NamedReaders. It is used to process + // this data and write the result to PerformanceAnalysis. class MergeReader : public Reader { public: MergeReader(const void *shared, size_t size, Merger &merger); + + // TODO: consider moving dump to ReportPerformance + void dump(int fd, size_t indent = 0); + // process a particular snapshot of the reader + void getAndProcessSnapshot(Snapshot & snap); + // call getSnapshot of the content of the reader's buffer and process the data + void getAndProcessSnapshot(); + private: // FIXME Needs to be protected by a lock, // because even though our use of it is read-only there may be asynchronous updates const std::vector& mNamedReaders; + + // analyzes, compresses and stores the merged data + std::map mThreadPerformanceAnalysis; + // handle author entry by looking up the author's name and appending it to the body // returns number of bytes read from fmtEntry void handleAuthor(const AbstractEntry &fmtEntry, String8 *body); @@ -552,7 +570,7 @@ public: // The thread is triggered on AudioFlinger binder activity. class MergeThread : public Thread { public: - MergeThread(Merger &merger); + MergeThread(Merger &merger, MergeReader &mergeReader); virtual ~MergeThread() override; // Reset timeout and activate thread to merge periodically if it's idle @@ -567,6 +585,9 @@ public: // the merger who actually does the work of merging the logs Merger& mMerger; + // the mergereader used to process data merged by mMerger + MergeReader& mMergeReader; + // mutex for the condition variable Mutex mMutex; diff --git a/media/libnbaio/include/media/nbaio/PerformanceAnalysis.h b/media/libnbaio/include/media/nbaio/PerformanceAnalysis.h index b0dc148189..d523aa940d 100644 --- a/media/libnbaio/include/media/nbaio/PerformanceAnalysis.h +++ b/media/libnbaio/include/media/nbaio/PerformanceAnalysis.h @@ -19,11 +19,11 @@ #ifndef ANDROID_MEDIA_PERFORMANCEANALYSIS_H #define ANDROID_MEDIA_PERFORMANCEANALYSIS_H -#include #include +#include #include -#include "NBLog.h" -#include "ReportPerformance.h" + +#include namespace android { diff --git a/media/libnbaio/include/media/nbaio/ReportPerformance.h b/media/libnbaio/include/media/nbaio/ReportPerformance.h index 27d2810154..e5b98406b9 100644 --- a/media/libnbaio/include/media/nbaio/ReportPerformance.h +++ b/media/libnbaio/include/media/nbaio/ReportPerformance.h @@ -24,7 +24,7 @@ namespace android { // This class is used by reportPerformance function -// TODO move reportPerformance function to ReportPerformance.cpp +// TODO move PerformanceAnalysis::reportPerformance function to ReportPerformance.cpp class String8; namespace ReportPerformance { @@ -52,10 +52,12 @@ static inline uint32_t log2(uint32_t x) { return 31 - __builtin_clz(x); } +// TODO: delete dump in NBLog::Reader and add it here + // Writes outlier intervals, timestamps, and histograms spanning long time // intervals to a file. -void writeToFile(std::deque> &outlierData, - std::deque> &hists, +void writeToFile(const std::deque> &outlierData, + const std::deque> &hists, const char * kName, bool append); diff --git a/services/medialog/MediaLogService.cpp b/services/medialog/MediaLogService.cpp index a5512e1207..fe19b6b582 100644 --- a/services/medialog/MediaLogService.cpp +++ b/services/medialog/MediaLogService.cpp @@ -26,13 +26,19 @@ namespace android { - static const char kDeadlockedString[] = "MediaLogService may be deadlocked\n"; +static const char kDeadlockedString[] = "MediaLogService may be deadlocked\n"; + +// mMerger, mMergeReader, and mMergeThread all point to the same location in memory +// mMergerShared. This is the local memory FIFO containing data merged from all +// individual thread FIFOs in shared memory. mMergeThread is used to periodically +// call NBLog::Merger::merge() to collect the data and write it to the FIFO, and call +// NBLog::MergeReader::getAndProcessSnapshot to process the merged data. MediaLogService::MediaLogService() : BnMediaLogService(), mMergerShared((NBLog::Shared*) malloc(NBLog::Timeline::sharedSize(kMergeBufferSize))), mMerger(mMergerShared, kMergeBufferSize), mMergeReader(mMergerShared, kMergeBufferSize, mMerger), - mMergeThread(new NBLog::MergeThread(mMerger)) + mMergeThread(new NBLog::MergeThread(mMerger, mMergeReader)) { mMergeThread->run("MergeThread"); } @@ -123,15 +129,10 @@ status_t MediaLogService::dump(int fd, const Vector& args __unused) } else { ALOGI("%s:", namedReader.name()); } - // TODO This code is for testing, remove it when done - // namedReader.reader()->dump(fd, 0 /*indent*/); } - mLock.unlock(); } } - - // FIXME request merge to make sure log is up to date mMergeReader.dump(fd); return NO_ERROR; } -- GitLab From 424c4f5b76a6ed11f2c713b42246a7220cfbb240 Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Wed, 19 Jul 2017 17:54:29 -0700 Subject: [PATCH 0006/1203] audio effects: Eliminate the cause warning logs about unreleased interface The cause of frequent "EffectModule 0xxx destructor called with unreleased interface" messages was due to not releasing the effects when purging stale effects. The cause of "Effect handle 0xxx disconnected after thread destruction" message was due to late binder call for disconnecting already purged effect handle. Also improved logging to communicate uuids of the effects causing these issues. Bug: 62267926 Test: no aforementioned warnings in the log when opening the Effects panel in Play Music Change-Id: I6ec6f60c46dc704226931fb59a641e4cd74c2fd1 --- media/libaudioclient/AudioEffect.cpp | 6 +++++- services/audioflinger/AudioFlinger.cpp | 2 +- services/audioflinger/Effects.cpp | 27 ++++++++++++-------------- 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/media/libaudioclient/AudioEffect.cpp b/media/libaudioclient/AudioEffect.cpp index a5f9ab6874..b1cb0e7bdd 100644 --- a/media/libaudioclient/AudioEffect.cpp +++ b/media/libaudioclient/AudioEffect.cpp @@ -135,7 +135,11 @@ status_t AudioEffect::set(const effect_uuid_t *type, &mStatus, &mId, &enabled); if (iEffect == 0 || (mStatus != NO_ERROR && mStatus != ALREADY_EXISTS)) { - ALOGE("set(): AudioFlinger could not create effect, status: %d", mStatus); + char typeBuffer[64], uuidBuffer[64]; + guidToString(type, typeBuffer, sizeof(typeBuffer)); + guidToString(uuid, uuidBuffer, sizeof(uuidBuffer)); + ALOGE("set(): AudioFlinger could not create effect %s / %s, status: %d", + typeBuffer, uuidBuffer, mStatus); if (iEffect == 0) { mStatus = NO_INIT; } diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 0df9a39c92..3a95a3b219 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -2603,7 +2603,7 @@ void AudioFlinger::purgeStaleEffects_l() { while (ec->mEffects.size()) { sp effect = ec->mEffects[0]; effect->unPin(); - t->removeEffect_l(effect); + t->removeEffect_l(effect, /*release*/ true); if (effect->purgeHandles()) { t->checkSuspendOnEffectEnabled_l(effect, false, effect->sessionId()); } diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp index f2c1c4fbce..bd5f146a82 100644 --- a/services/audioflinger/Effects.cpp +++ b/services/audioflinger/Effects.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -109,7 +110,10 @@ AudioFlinger::EffectModule::~EffectModule() { ALOGV("Destructor %p", this); if (mEffectInterface != 0) { - ALOGW("EffectModule %p destructor called with unreleased interface", this); + char uuidStr[64]; + AudioEffect::guidToString(&mDescriptor.uuid, uuidStr, sizeof(uuidStr)); + ALOGW("EffectModule %p destructor called with unreleased interface, effect %s", + this, uuidStr); release_l(); } @@ -1081,18 +1085,12 @@ void AudioFlinger::EffectModule::dump(int fd, const Vector& args __unu result.append(buffer); result.append("\t\tDescriptor:\n"); - snprintf(buffer, SIZE, "\t\t- UUID: %08X-%04X-%04X-%04X-%02X%02X%02X%02X%02X%02X\n", - mDescriptor.uuid.timeLow, mDescriptor.uuid.timeMid, mDescriptor.uuid.timeHiAndVersion, - mDescriptor.uuid.clockSeq, mDescriptor.uuid.node[0], mDescriptor.uuid.node[1], - mDescriptor.uuid.node[2], - mDescriptor.uuid.node[3],mDescriptor.uuid.node[4],mDescriptor.uuid.node[5]); + char uuidStr[64]; + AudioEffect::guidToString(&mDescriptor.uuid, uuidStr, sizeof(uuidStr)); + snprintf(buffer, SIZE, "\t\t- UUID: %s\n", uuidStr); result.append(buffer); - snprintf(buffer, SIZE, "\t\t- TYPE: %08X-%04X-%04X-%04X-%02X%02X%02X%02X%02X%02X\n", - mDescriptor.type.timeLow, mDescriptor.type.timeMid, - mDescriptor.type.timeHiAndVersion, - mDescriptor.type.clockSeq, mDescriptor.type.node[0], mDescriptor.type.node[1], - mDescriptor.type.node[2], - mDescriptor.type.node[3],mDescriptor.type.node[4],mDescriptor.type.node[5]); + AudioEffect::guidToString(&mDescriptor.type, uuidStr, sizeof(uuidStr)); + snprintf(buffer, SIZE, "\t\t- TYPE: %s\n", uuidStr); result.append(buffer); snprintf(buffer, SIZE, "\t\t- apiVersion: %08X\n\t\t- flags: %08X (%s)\n", mDescriptor.apiVersion, @@ -1306,11 +1304,10 @@ void AudioFlinger::EffectHandle::disconnect(bool unpinIfLast) if (thread != 0) { thread->disconnectEffectHandle(this, unpinIfLast); } else { - ALOGW("%s Effect handle %p disconnected after thread destruction", __FUNCTION__, this); // try to cleanup as much as we can sp effect = mEffect.promote(); - if (effect != 0) { - effect->disconnectHandle(this, unpinIfLast); + if (effect != 0 && effect->disconnectHandle(this, unpinIfLast) > 0) { + ALOGW("%s Effect handle %p disconnected after thread destruction", __FUNCTION__, this); } } -- GitLab From 16d43d99caf1cfb17f5774387fb60403b9cd371a Mon Sep 17 00:00:00 2001 From: Derek Sollenberger Date: Mon, 24 Jul 2017 10:36:43 -0400 Subject: [PATCH 0007/1203] Reduce overhead by combining libskia and libhwui into a single library. This is a multiproject change as we need to both the libraries themselves as well as those that had dependencies on libskia.so Bug: 31971097 Test: compile only Change-Id: Ib978e42dd90d63d41616edb481c1accae3515341 --- media/ndk/Android.bp | 1 - 1 file changed, 1 deletion(-) diff --git a/media/ndk/Android.bp b/media/ndk/Android.bp index 40974f383b..2d9474437e 100644 --- a/media/ndk/Android.bp +++ b/media/ndk/Android.bp @@ -69,7 +69,6 @@ cc_library_shared { "libmedia", "libmedia_jni", "libmediadrm", - "libskia", "libstagefright", "libstagefright_foundation", "liblog", -- GitLab From 5f648974c0583cdae58b263490519a544b453690 Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Fri, 21 Jul 2017 15:42:59 -0700 Subject: [PATCH 0008/1203] mediaplayer: save thread id in MediaPlayer::reset() Fix: 63905551 Test: adb shell am instrument -e size small -w 'android.media.cts/android.support.test.runner.AndroidJUnitRunner' Change-Id: I54f724d2b2e73e2ab08aa7cc07054afe6e0a8092 --- media/libmedia/mediaplayer.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp index b976721cd1..efe947adc8 100644 --- a/media/libmedia/mediaplayer.cpp +++ b/media/libmedia/mediaplayer.cpp @@ -649,8 +649,12 @@ status_t MediaPlayer::doSetRetransmitEndpoint(const sp& player) { status_t MediaPlayer::reset() { ALOGV("reset"); + mLockThreadId = getThreadId(); Mutex::Autolock _l(mLock); - return reset_l(); + status_t result = reset_l(); + mLockThreadId = 0; + + return result; } status_t MediaPlayer::setAudioStreamType(audio_stream_type_t type) @@ -860,7 +864,7 @@ void MediaPlayer::notify(int msg, int ext1, int ext2, const Parcel *obj) // this will deadlock. // // The threadId hack below works around this for the care of prepare, - // seekTo and start within the same process. + // seekTo, start, and reset within the same process. // FIXME: Remember, this is a hack, it's not even a hack that is applied // consistently for all use-cases, this needs to be revisited. if (mLockThreadId != getThreadId()) { -- GitLab From a80649ad90956a25e24c2cd00931b0253e994721 Mon Sep 17 00:00:00 2001 From: Sanna Catherine de Treville Wager Date: Fri, 21 Jul 2017 16:16:38 -0700 Subject: [PATCH 0009/1203] Simplify processAndFlushTimeStampSeries Instead first writing to a deque of short-term histograms and copying the data over to long-term histograms, write directly to a single deque of histograms. Test: dumpsys media.log Change-Id: I05a283a54b84bc7d061c7fae7b671390c1690def --- media/libnbaio/PerformanceAnalysis.cpp | 95 +++++++------------ .../include/media/nbaio/PerformanceAnalysis.h | 29 +++--- 2 files changed, 48 insertions(+), 76 deletions(-) diff --git a/media/libnbaio/PerformanceAnalysis.cpp b/media/libnbaio/PerformanceAnalysis.cpp index d44dd45f98..41184a633b 100644 --- a/media/libnbaio/PerformanceAnalysis.cpp +++ b/media/libnbaio/PerformanceAnalysis.cpp @@ -54,16 +54,6 @@ PerformanceAnalysis::PerformanceAnalysis() { kPeriodMsCPU = static_cast(kPeriodMs * kRatio); } -// converts a time series into a map. key: buffer period length. value: count -static std::map buildBuckets(const std::vector &samples) { - // TODO allow buckets of variable resolution - std::map buckets; - for (size_t i = 1; i < samples.size(); ++i) { - ++buckets[deltaMs(samples[i - 1], samples[i])]; - } - return buckets; -} - static int widthOf(int x) { int width = 0; while (x > 0) { @@ -79,21 +69,41 @@ static int widthOf(int x) { // small or large values and stores these as peaks, and flushes // the timestamp series from memory. void PerformanceAnalysis::processAndFlushTimeStampSeries() { + if (mTimeStampSeries.empty()) { + ALOGD("Timestamp series is empty"); + return; + } + + // mHists is empty if program just started + if (mHists.empty()) { + mHists.emplace_front(static_cast(mTimeStampSeries[0]), + std::map()); + } + // 1) analyze the series to store all outliers and their exact timestamps: storeOutlierData(mTimeStampSeries); // 2) detect peaks in the outlier series detectPeaks(); - // 3) compute its histogram, append to mRecentHists and clear the time series - mRecentHists.emplace_back(static_cast(mTimeStampSeries[0]), - buildBuckets(mTimeStampSeries)); - // do not let mRecentHists exceed capacity - // ALOGD("mRecentHists size: %d", static_cast(mRecentHists.size())); - if (mRecentHists.size() >= kRecentHistsCapacity) { - // ALOGD("popped back mRecentHists"); - mRecentHists.pop_front(); + // if the current histogram has spanned its maximum time interval, + // insert a new empty histogram to the front of mHists + if (deltaMs(mHists[0].first, mTimeStampSeries[0]) >= kMaxHistTimespanMs) { + mHists.emplace_front(static_cast(mTimeStampSeries[0]), + std::map()); + // When memory is full, delete oldest histogram + if (mHists.size() >= kHistsCapacity) { + mHists.resize(kHistsCapacity); + } } + + // 3) add current time intervals to histogram + for (size_t i = 1; i < mTimeStampSeries.size(); ++i) { + ++mHists[0].second[deltaMs( + mTimeStampSeries[i - 1], mTimeStampSeries[i])]; + } + + // clear the timestamps mTimeStampSeries.clear(); } @@ -116,48 +126,14 @@ void PerformanceAnalysis::logTsEntry(int64_t ts) { mTimeStampSeries.push_back(ts); // if length of the time series has reached kShortHistSize samples, // analyze the data and flush the timestamp series from memory - if (mTimeStampSeries.size() >= kShortHistSize) { + if (mTimeStampSeries.size() >= kHistSize) { processAndFlushTimeStampSeries(); } } -// When the short-term histogram array mRecentHists has reached capacity, -// merge histograms for data compression and store them in mLongTermHists -// clears mRecentHists -// TODO: have logTsEntry write directly to mLongTermHists, discard mRecentHists, -// start a new histogram when a peak occurs -void PerformanceAnalysis::processAndFlushRecentHists() { - - // Buckets is used to aggregate short-term histograms. - Histogram buckets; - timestamp startingTs = mRecentHists[0].first; - - for (const auto &shortHist: mRecentHists) { - // If the time between starting and ending timestamps has reached the maximum, - // add the current histogram (buckets) to the long-term histogram buffer, - // clear buckets, and start a new long-term histogram aggregation process. - if (deltaMs(startingTs, shortHist.first) >= kMaxHistTimespanMs) { - mLongTermHists.emplace_back(startingTs, std::move(buckets)); - buckets.clear(); - startingTs = shortHist.first; - // When memory is full, delete oldest histogram - // TODO use a circular buffer - if (mLongTermHists.size() >= kLongTermHistsCapacity) { - mLongTermHists.pop_front(); - } - } - - // add current histogram to buckets - for (const auto &countPair : shortHist.second) { - buckets[countPair.first] += countPair.second; - } - } - mRecentHists.clear(); - // TODO: decide when/where to call writeToFile - // TODO: add a thread-specific extension to the file name - static const char* const kName = (const char *) "/data/misc/audioserver/sample_results.txt"; - writeToFile(mOutlierData, mLongTermHists, kName, false); -} +// TODO: move this someplace +// static const char* const kName = (const char *) "/data/misc/audioserver/sample_results.txt"; +// writeToFile(mOutlierData, mLongTermHists, kName, false); // Given a series of outlier intervals (mOutlier data), // looks for changes in distribution (peaks), which can be either positive or negative. @@ -267,13 +243,14 @@ void PerformanceAnalysis::testFunction() { // TODO consider changing all ints to uint32_t or uint64_t // TODO: move this to ReportPerformance, probably make it a friend function of PerformanceAnalysis void PerformanceAnalysis::reportPerformance(String8 *body, int maxHeight) { - if (mRecentHists.size() < 1) { - ALOGD("reportPerformance: mRecentHists is empty"); + if (mHists.empty()) { + ALOGD("reportPerformance: mHists is empty"); return; } + ALOGD("reportPerformance: hists size %d", static_cast(mHists.size())); // TODO: more elaborate data analysis std::map buckets; - for (const auto &shortHist: mRecentHists) { + for (const auto &shortHist: mHists) { for (const auto &countPair : shortHist.second) { buckets[countPair.first] += countPair.second; } diff --git a/media/libnbaio/include/media/nbaio/PerformanceAnalysis.h b/media/libnbaio/include/media/nbaio/PerformanceAnalysis.h index d523aa940d..0f47619165 100644 --- a/media/libnbaio/include/media/nbaio/PerformanceAnalysis.h +++ b/media/libnbaio/include/media/nbaio/PerformanceAnalysis.h @@ -43,16 +43,17 @@ public: // the timestamp series from memory. void processAndFlushTimeStampSeries(); + // Given a series of audio processing wakeup timestamps, + // compresses and and analyzes the data, and flushes + // the timestamp series from memory. + void processAndFlushTimeStampSeriesOld(); + // Called when an audio on/off event is read from the buffer, // e.g. EVENT_AUDIO_STATE. // calls flushTimeStampSeries on the data up to the event, // effectively discarding the idle audio time interval void handleStateChange(); - // When the short-term histogram array mRecentHists has reached capacity, - // merges histograms for data compression and stores them in mLongTermHists - void processAndFlushRecentHists(); - // Writes wakeup timestamp entry to log and runs analysis // TODO: make this thread safe. Each thread should have its own instance // of PerformanceAnalysis. @@ -90,16 +91,11 @@ private: // a peak is a moment at which the average outlier interval changed significantly std::deque mPeakTimestamps; - // TODO: turn these into circular buffers for better data flow - // FIFO of small histograms - // stores fixed-size short buffer period histograms with timestamp of first sample - std::deque> mRecentHists; - - // FIFO of small histograms - // stores fixed-size long-term buffer period histograms with timestamp of first sample - std::deque> mLongTermHists; + // stores stores buffer period histograms with timestamp of first sample + // TODO use a circular buffer + std::deque> mHists; - // vector of timestamps, collected from NBLog for a (TODO) specific thread + // vector of timestamps, collected from NBLog for a specific thread // when a vector reaches its maximum size, the data is processed and flushed std::vector mTimeStampSeries; @@ -119,12 +115,11 @@ private: static const int kStddevThreshold = 5; // capacity allocated to data structures - // TODO: adjust all of these values - static const int kRecentHistsCapacity = 100; // number of short-term histograms stored in memory - static const int kShortHistSize = 50; // number of samples in a short-term histogram + // TODO: make these values longer when testing is finished + static const int kHistsCapacity = 20; // number of short-term histograms stored in memory + static const int kHistSize = 1000; // max number of samples stored in a histogram static const int kOutlierSeriesSize = 100; // number of values stored in outlier array static const int kPeakSeriesSize = 100; // number of values stored in peak array - static const int kLongTermHistsCapacity = 20; // number of long-term histogram stored in memory // maximum elapsed time between first and last timestamp of a long-term histogram static const int kMaxHistTimespanMs = 5 * kMsPerSec; -- GitLab From cf6c75a45416a90d2afdf42f0ac2f0b26f67839a Mon Sep 17 00:00:00 2001 From: Sanna Catherine de Treville Wager Date: Fri, 21 Jul 2017 17:05:25 -0700 Subject: [PATCH 0010/1203] Moved dump from NBLog to ReportPerformance Minor cleanup Test: dumpsys media.log Change-Id: Ib4d12f6e20c04ecb3344290083ea65a3fe1e47e0 --- media/libnbaio/NBLog.cpp | 19 ++------------- media/libnbaio/PerformanceAnalysis.cpp | 23 +++++++++++++++++-- media/libnbaio/include/media/nbaio/NBLog.h | 6 ++--- .../include/media/nbaio/PerformanceAnalysis.h | 7 +++++- 4 files changed, 32 insertions(+), 23 deletions(-) diff --git a/media/libnbaio/NBLog.cpp b/media/libnbaio/NBLog.cpp index f00d86fec9..0adeb46bc8 100644 --- a/media/libnbaio/NBLog.cpp +++ b/media/libnbaio/NBLog.cpp @@ -92,7 +92,6 @@ #include #include #include -// #include #include #include #include @@ -941,22 +940,8 @@ void NBLog::MergeReader::getAndProcessSnapshot() getAndProcessSnapshot(*snap); } -// TODO: move this function to a different class than NBLog::Reader -// writes summary of performance to the console -void NBLog::MergeReader::dump(int fd, size_t indent) -{ - mFd = fd; - mIndent = indent; - String8 body, timestamp; - // TODO: check: is the FIXME below still a problem? - // FIXME: this is not thread safe - for (auto & threadReport : mThreadPerformanceAnalysis) { - threadReport.second.reportPerformance(&body); - } - if (!body.isEmpty()) { - ALOGD("body is not empty"); - dumpLine(timestamp, body); - } +void NBLog::MergeReader::dump(int fd, int indent) { + ReportPerformance::dump(fd, indent, mThreadPerformanceAnalysis); } // Writes a string to the console diff --git a/media/libnbaio/PerformanceAnalysis.cpp b/media/libnbaio/PerformanceAnalysis.cpp index 41184a633b..37d6d9f176 100644 --- a/media/libnbaio/PerformanceAnalysis.cpp +++ b/media/libnbaio/PerformanceAnalysis.cpp @@ -36,7 +36,6 @@ #include #include #include -// #include // used to print callstack #include #include @@ -242,7 +241,7 @@ void PerformanceAnalysis::testFunction() { // TODO Make it return a std::string instead of modifying body --> is this still relevant? // TODO consider changing all ints to uint32_t or uint64_t // TODO: move this to ReportPerformance, probably make it a friend function of PerformanceAnalysis -void PerformanceAnalysis::reportPerformance(String8 *body, int maxHeight) { +void PerformanceAnalysis::reportPerformance(String8 *body, int maxHeight) const { if (mHists.empty()) { ALOGD("reportPerformance: mHists is empty"); return; @@ -346,6 +345,26 @@ void PerformanceAnalysis::alertIfGlitch(const std::vector &samples) { return; } +//------------------------------------------------------------------------------ + +// writes summary of performance into specified file descriptor +void dump(int fd, int indent, const std::map + &threadPerformanceAnalysis) { + String8 body; + for (auto & thread : threadPerformanceAnalysis) { + thread.second.reportPerformance(&body); + } + if (!body.isEmpty()) { + dumpLine(fd, indent, body); + body.clear(); + } +} + +// Writes a string into specified file descriptor +void dumpLine(int fd, int indent, const String8 &body) { + dprintf(fd, "%.*s%s \n", indent, "", body.string()); +} + } // namespace ReportPerformance } // namespace android diff --git a/media/libnbaio/include/media/nbaio/NBLog.h b/media/libnbaio/include/media/nbaio/NBLog.h index 1504f81389..b4af58b49d 100644 --- a/media/libnbaio/include/media/nbaio/NBLog.h +++ b/media/libnbaio/include/media/nbaio/NBLog.h @@ -452,10 +452,11 @@ public: // get snapshot of readers fifo buffer, effectively consuming the buffer std::unique_ptr getSnapshot(); - // print a summary of the performance to the console + bool isIMemory(const sp& iMemory) const; protected: + // print a summary of the performance to the console void dumpLine(const String8& timestamp, String8& body); EntryIterator handleFormat(const FormatEntry &fmtEntry, String8 *timestamp, @@ -544,8 +545,7 @@ public: public: MergeReader(const void *shared, size_t size, Merger &merger); - // TODO: consider moving dump to ReportPerformance - void dump(int fd, size_t indent = 0); + void dump(int fd, int indent = 0); // process a particular snapshot of the reader void getAndProcessSnapshot(Snapshot & snap); // call getSnapshot of the content of the reader's buffer and process the data diff --git a/media/libnbaio/include/media/nbaio/PerformanceAnalysis.h b/media/libnbaio/include/media/nbaio/PerformanceAnalysis.h index 0f47619165..67bd3ac5bb 100644 --- a/media/libnbaio/include/media/nbaio/PerformanceAnalysis.h +++ b/media/libnbaio/include/media/nbaio/PerformanceAnalysis.h @@ -73,7 +73,7 @@ public: // input: series of short histograms. Generates a string of analysis of the buffer periods // TODO: WIP write more detailed analysis // FIXME: move this data visualization to a separate class. Model/view/controller - void reportPerformance(String8 *body, int maxHeight = 10); + void reportPerformance(String8 *body, int maxHeight = 10) const; // TODO: delete this. temp for testing void testFunction(); @@ -134,6 +134,11 @@ private: }; +void dump(int fd, int indent, const std::map + &threadPerformanceAnalysis); + +void dumpLine(int fd, int indent, const String8 &body); + } // namespace ReportPerformance } // namespace android -- GitLab From d096517201332e03679c913e0d098c61ec6e4f0c Mon Sep 17 00:00:00 2001 From: Sanna Catherine de Treville Wager Date: Mon, 24 Jul 2017 13:42:44 -0700 Subject: [PATCH 0011/1203] Separate analysis for each source file location Use a map of maps of PerformanceAnalysis. The first key is the thread, the second key the source file location. Test: dumpsys media.log Change-Id: Ib60758e10ce1ddbf65337419db2ff3aa070763fb --- media/libnbaio/NBLog.cpp | 29 ++++++++++--------- media/libnbaio/PerformanceAnalysis.cpp | 7 +++-- media/libnbaio/include/media/nbaio/NBLog.h | 4 ++- .../include/media/nbaio/PerformanceAnalysis.h | 7 +++-- 4 files changed, 27 insertions(+), 20 deletions(-) diff --git a/media/libnbaio/NBLog.cpp b/media/libnbaio/NBLog.cpp index 0adeb46bc8..08f0944e43 100644 --- a/media/libnbaio/NBLog.cpp +++ b/media/libnbaio/NBLog.cpp @@ -49,20 +49,25 @@ * * 2) reading the data from shared memory * Thread::threadloop() -* TODO: add description? * NBLog::MergeThread::threadLoop() -* calls NBLog::Merger::merge +* Waits on a mutex, called periodically +* Calls NBLog::Merger::merge and MergeReader.getAndProcessSnapshot. * NBLog::Merger::merge * Merges snapshots sorted by timestamp -* for each reader in vector of class NamedReader, -* callsNamedReader::reader()->getSnapshot -* TODO: check whether the rest of this function is relevant +* Calls Reader::getSnapshot on each individual thread buffer to in shared +* memory and writes all their data to the single FIFO stored in mMerger. * NBLog::Reader::getSnapshot * copies snapshot of reader's fifo buffer into its own buffer * calls mFifoReader->obtain to find readable data * sets snapshot.begin() and .end() iterators to boundaries of valid entries * moves the fifo reader index to after the last entry read * in this case, the buffer is in shared memory. in (4), the buffer is private +* NBLog::MergeThread::getAndProcessSnapshot +* Iterates through the entries in the local FIFO. Processes the data in +* specific ways depending on the entry type. If the data is a histogram +* timestamp or an audio on/off signal, writes to a map of PerformanceAnalysis +* class instances, where the wakeup() intervals are stored as histograms +* and analyzed. * * 3) reading the data from private buffer * MediaLogService::dump @@ -876,8 +881,7 @@ std::unique_ptr NBLog::Reader::getSnapshot() void NBLog::MergeReader::getAndProcessSnapshot(NBLog::Reader::Snapshot &snapshot) { String8 timestamp, body; - // TODO: check: is the FIXME below still a problem? - // FIXME: this is not thread safe + // TODO: check: is this thread safe? // TODO: add lost data information and notification to ReportPerformance size_t lost = snapshot.lost() + (snapshot.begin() - EntryIterator(snapshot.data())); if (lost > 0) { @@ -899,7 +903,7 @@ void NBLog::MergeReader::getAndProcessSnapshot(NBLog::Reader::Snapshot &snapshot memcpy(&hash, &(data->hash), sizeof(hash)); int64_t ts; memcpy(&ts, &data->ts, sizeof(ts)); - mThreadPerformanceAnalysis[data->author].logTsEntry(ts); + mThreadPerformanceAnalysis[data->author][hash].logTsEntry(ts); ++entry; break; } @@ -907,12 +911,9 @@ void NBLog::MergeReader::getAndProcessSnapshot(NBLog::Reader::Snapshot &snapshot HistTsEntryWithAuthor *data = (HistTsEntryWithAuthor *) (entry->data); // TODO This memcpies are here to avoid unaligned memory access crash. // There's probably a more efficient way to do it - // TODO: incorporate hash information in mThreadPerformanceAnalysis - // log_hash_t hash; - // memcpy(&hash, &(data->hash), sizeof(hash)); - // int64_t ts; - // memcpy(&ts, &data->ts, sizeof(ts)); - mThreadPerformanceAnalysis[data->author].handleStateChange(); + log_hash_t hash; + memcpy(&hash, &(data->hash), sizeof(hash)); + mThreadPerformanceAnalysis[data->author][hash].handleStateChange(); ++entry; break; } diff --git a/media/libnbaio/PerformanceAnalysis.cpp b/media/libnbaio/PerformanceAnalysis.cpp index 37d6d9f176..698d5922d8 100644 --- a/media/libnbaio/PerformanceAnalysis.cpp +++ b/media/libnbaio/PerformanceAnalysis.cpp @@ -348,11 +348,12 @@ void PerformanceAnalysis::alertIfGlitch(const std::vector &samples) { //------------------------------------------------------------------------------ // writes summary of performance into specified file descriptor -void dump(int fd, int indent, const std::map - &threadPerformanceAnalysis) { +void dump(int fd, int indent, const PerformanceAnalysisMap &threadPerformanceAnalysis) { String8 body; for (auto & thread : threadPerformanceAnalysis) { - thread.second.reportPerformance(&body); + for (auto & hash: thread.second) { + hash.second.reportPerformance(&body); + } } if (!body.isEmpty()) { dumpLine(fd, indent, body); diff --git a/media/libnbaio/include/media/nbaio/NBLog.h b/media/libnbaio/include/media/nbaio/NBLog.h index b4af58b49d..bd17674d5d 100644 --- a/media/libnbaio/include/media/nbaio/NBLog.h +++ b/media/libnbaio/include/media/nbaio/NBLog.h @@ -557,7 +557,9 @@ public: const std::vector& mNamedReaders; // analyzes, compresses and stores the merged data - std::map mThreadPerformanceAnalysis; + // contains a separate instance for every author (thread), and for every source file + // location within each author + ReportPerformance::PerformanceAnalysisMap mThreadPerformanceAnalysis; // handle author entry by looking up the author's name and appending it to the body // returns number of bytes read from fmtEntry diff --git a/media/libnbaio/include/media/nbaio/PerformanceAnalysis.h b/media/libnbaio/include/media/nbaio/PerformanceAnalysis.h index 67bd3ac5bb..dff307deba 100644 --- a/media/libnbaio/include/media/nbaio/PerformanceAnalysis.h +++ b/media/libnbaio/include/media/nbaio/PerformanceAnalysis.h @@ -134,8 +134,11 @@ private: }; -void dump(int fd, int indent, const std::map - &threadPerformanceAnalysis); +// a map of PerformanceAnalysis instances +// The outer key is for the thread, the inner key for the source file location. +using PerformanceAnalysisMap = std::map>; + +void dump(int fd, int indent, const PerformanceAnalysisMap &threadPerformanceAnalysis); void dumpLine(int fd, int indent, const String8 &body); -- GitLab From e0ecc35b14f581ab7adf1c131fd6de24c9f8dbb5 Mon Sep 17 00:00:00 2001 From: Ian Elliott Date: Tue, 18 Jul 2017 15:53:06 -0600 Subject: [PATCH 0012/1203] Have the Surface class track the buffer age. This change corresponds to a change between the Surface and BufferQueueProducer classes. Have the Surface class track the buffer age, so that Surface::query() can return the buffer age without having to use a binder call to BufferQueueProducer::query(). The idea is for BufferQueueProducer::dequeueBuffer() to return the value, which the Surface class will cache for later use by Surface::query(). Bug: b/27903668 Test: Use systrace to no ensure query binder call after dequeueBuffer. Change-Id: Iee7e7a3f92dcc88db70f460186a63450a6f18e31 --- media/libstagefright/omx/1.0/WGraphicBufferProducer.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/media/libstagefright/omx/1.0/WGraphicBufferProducer.cpp b/media/libstagefright/omx/1.0/WGraphicBufferProducer.cpp index acda060133..c11c986700 100644 --- a/media/libstagefright/omx/1.0/WGraphicBufferProducer.cpp +++ b/media/libstagefright/omx/1.0/WGraphicBufferProducer.cpp @@ -64,10 +64,9 @@ Return TWGraphicBufferProducer::dequeueBuffer( sp fence; ::android::FrameEventHistoryDelta outTimestamps; status_t status = mBase->dequeueBuffer( - &slot, &fence, - width, height, - static_cast<::android::PixelFormat>(format), usage, - getFrameTimestamps ? &outTimestamps : nullptr); + &slot, &fence, width, height, + static_cast<::android::PixelFormat>(format), usage, nullptr, + getFrameTimestamps ? &outTimestamps : nullptr); hidl_handle tFence; FrameEventHistoryDelta tOutTimestamps; -- GitLab From 23f89d3469e5f4de4072bbd305e9552483abe723 Mon Sep 17 00:00:00 2001 From: Sanna Catherine de Treville Wager Date: Mon, 24 Jul 2017 18:24:48 -0700 Subject: [PATCH 0013/1203] Write thread and data to separate files Each file name starts with either "histogram" or "outlier" and contains thread and hash (source file location) information. Test: dumpsys media.log Change-Id: I4bbea8b5265ac539e6fb2ce16207e4f5c89d21d4 --- media/libnbaio/PerformanceAnalysis.cpp | 23 ++++++----- media/libnbaio/ReportPerformance.cpp | 40 +++++++++++++------ .../include/media/nbaio/PerformanceAnalysis.h | 23 +++++------ .../include/media/nbaio/ReportPerformance.h | 3 +- 4 files changed, 53 insertions(+), 36 deletions(-) diff --git a/media/libnbaio/PerformanceAnalysis.cpp b/media/libnbaio/PerformanceAnalysis.cpp index 698d5922d8..d1dff47291 100644 --- a/media/libnbaio/PerformanceAnalysis.cpp +++ b/media/libnbaio/PerformanceAnalysis.cpp @@ -73,7 +73,7 @@ void PerformanceAnalysis::processAndFlushTimeStampSeries() { return; } - // mHists is empty if program just started + // mHists is empty if thread/hash pair is sending data for the first time if (mHists.empty()) { mHists.emplace_front(static_cast(mTimeStampSeries[0]), std::map()); @@ -130,10 +130,6 @@ void PerformanceAnalysis::logTsEntry(int64_t ts) { } } -// TODO: move this someplace -// static const char* const kName = (const char *) "/data/misc/audioserver/sample_results.txt"; -// writeToFile(mOutlierData, mLongTermHists, kName, false); - // Given a series of outlier intervals (mOutlier data), // looks for changes in distribution (peaks), which can be either positive or negative. // The function sets the mean to the starting value and sigma to 0, and updates @@ -223,7 +219,6 @@ void PerformanceAnalysis::storeOutlierData(const std::vector ×tamp } } - // FIXME: delete this temporary test code, recycled for various new functions void PerformanceAnalysis::testFunction() { // produces values (4: 5000000), (13: 18000000) @@ -241,7 +236,10 @@ void PerformanceAnalysis::testFunction() { // TODO Make it return a std::string instead of modifying body --> is this still relevant? // TODO consider changing all ints to uint32_t or uint64_t // TODO: move this to ReportPerformance, probably make it a friend function of PerformanceAnalysis -void PerformanceAnalysis::reportPerformance(String8 *body, int maxHeight) const { +void PerformanceAnalysis::reportPerformance(String8 *body, int maxHeight) { + // Add any new data + processAndFlushTimeStampSeries(); + if (mHists.empty()) { ALOGD("reportPerformance: mHists is empty"); return; @@ -348,11 +346,18 @@ void PerformanceAnalysis::alertIfGlitch(const std::vector &samples) { //------------------------------------------------------------------------------ // writes summary of performance into specified file descriptor -void dump(int fd, int indent, const PerformanceAnalysisMap &threadPerformanceAnalysis) { +void dump(int fd, int indent, PerformanceAnalysisMap &threadPerformanceAnalysis) { String8 body; + const char* const kName = "/data/misc/audioserver/"; for (auto & thread : threadPerformanceAnalysis) { for (auto & hash: thread.second) { - hash.second.reportPerformance(&body); + PerformanceAnalysis& curr = hash.second; + curr.processAndFlushTimeStampSeries(); + // write performance data to console + curr.reportPerformance(&body); + // write to file + writeToFile(curr.mOutlierData, curr.mHists, kName, false, + thread.first, hash.first); } } if (!body.isEmpty()) { diff --git a/media/libnbaio/ReportPerformance.cpp b/media/libnbaio/ReportPerformance.cpp index 7d3869ce4a..87c223bfec 100644 --- a/media/libnbaio/ReportPerformance.cpp +++ b/media/libnbaio/ReportPerformance.cpp @@ -23,12 +23,12 @@ #include #include #include +#include #include #include #include #include #include -// #include // used to print callstack #include #include @@ -40,34 +40,48 @@ namespace ReportPerformance { // TODO: format the data efficiently and write different types of data to different files void writeToFile(const std::deque> &outlierData, const std::deque> &hists, - const char * kName, - bool append) { - ALOGD("writing performance data to file"); + const char * kDirectory, + bool append, int author, log_hash_t hash) { if (outlierData.empty() || hists.empty()) { + ALOGW("No data, returning."); return; } + std::stringstream outlierName; + std::stringstream histogramName; + + outlierName << kDirectory << "outliers_" << author << "_" << hash; + histogramName << kDirectory << "histograms_" << author << "_" << hash; + std::ofstream ofs; - ofs.open(kName, append ? std::ios::app : std::ios::trunc); + ofs.open(outlierName.str().c_str(), append ? std::ios::app : std::ios::trunc); if (!ofs.is_open()) { - ALOGW("couldn't open file %s", kName); + ALOGW("couldn't open file %s", outlierName.str().c_str()); return; } ofs << "Outlier data: interval and timestamp\n"; for (const auto &outlier : outlierData) { ofs << outlier.first << ": " << outlier.second << "\n"; } - ofs << "Histogram data\n"; + ofs.close(); + + std::ofstream hfs; + hfs.open(histogramName.str().c_str(), append ? std::ios::app : std::ios::trunc); + if (!hfs.is_open()) { + ALOGW("couldn't open file %s", histogramName.str().c_str()); + return; + } + hfs << "Histogram data\n"; for (const auto &hist : hists) { - ofs << "\ttimestamp\n"; - ofs << hist.first << "\n"; - ofs << "\tbuckets and counts\n"; + hfs << "\ttimestamp\n"; + hfs << hist.first << "\n"; + hfs << "\tbuckets and counts\n"; for (const auto &bucket : hist.second) { - ofs << bucket.first << ": " << bucket.second << "\n"; + hfs << bucket.first << ": " << bucket.second << "\n"; } - ofs << "\n"; // separate histograms with a newline + hfs << "\n"; // separate histograms with a newline } - ofs.close(); + hfs.close(); } } // namespace ReportPerformance diff --git a/media/libnbaio/include/media/nbaio/PerformanceAnalysis.h b/media/libnbaio/include/media/nbaio/PerformanceAnalysis.h index dff307deba..da68821668 100644 --- a/media/libnbaio/include/media/nbaio/PerformanceAnalysis.h +++ b/media/libnbaio/include/media/nbaio/PerformanceAnalysis.h @@ -29,6 +29,12 @@ namespace android { namespace ReportPerformance { +class PerformanceAnalysis; + +// a map of PerformanceAnalysis instances +// The outer key is for the thread, the inner key for the source file location. +using PerformanceAnalysisMap = std::map>; + class PerformanceAnalysis { // This class stores and analyzes audio processing wakeup timestamps from NBLog // FIXME: currently, all performance data is stored in deques. Need to add a mutex. @@ -38,15 +44,13 @@ public: PerformanceAnalysis(); - // Given a series of audio processing wakeup timestamps, - // compresses and and analyzes the data, and flushes - // the timestamp series from memory. - void processAndFlushTimeStampSeries(); + friend void dump(int fd, int indent, + PerformanceAnalysisMap &threadPerformanceAnalysis); // Given a series of audio processing wakeup timestamps, // compresses and and analyzes the data, and flushes // the timestamp series from memory. - void processAndFlushTimeStampSeriesOld(); + void processAndFlushTimeStampSeries(); // Called when an audio on/off event is read from the buffer, // e.g. EVENT_AUDIO_STATE. @@ -73,7 +77,7 @@ public: // input: series of short histograms. Generates a string of analysis of the buffer periods // TODO: WIP write more detailed analysis // FIXME: move this data visualization to a separate class. Model/view/controller - void reportPerformance(String8 *body, int maxHeight = 10) const; + void reportPerformance(String8 *body, int maxHeight = 10); // TODO: delete this. temp for testing void testFunction(); @@ -134,12 +138,7 @@ private: }; -// a map of PerformanceAnalysis instances -// The outer key is for the thread, the inner key for the source file location. -using PerformanceAnalysisMap = std::map>; - -void dump(int fd, int indent, const PerformanceAnalysisMap &threadPerformanceAnalysis); - +void dump(int fd, int indent, PerformanceAnalysisMap &threadPerformanceAnalysis); void dumpLine(int fd, int indent, const String8 &body); } // namespace ReportPerformance diff --git a/media/libnbaio/include/media/nbaio/ReportPerformance.h b/media/libnbaio/include/media/nbaio/ReportPerformance.h index e5b98406b9..afbbb75a58 100644 --- a/media/libnbaio/include/media/nbaio/ReportPerformance.h +++ b/media/libnbaio/include/media/nbaio/ReportPerformance.h @@ -58,8 +58,7 @@ static inline uint32_t log2(uint32_t x) { // intervals to a file. void writeToFile(const std::deque> &outlierData, const std::deque> &hists, - const char * kName, - bool append); + const char * kName, bool append, int author, log_hash_t hash); } // namespace ReportPerformance -- GitLab From 6b5b6fdd3362094c3c994b8a2f79f6add08d4e95 Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Tue, 14 Feb 2017 11:06:25 +0900 Subject: [PATCH 0014/1203] codec2: switch to Android.bp Test: builds Change-Id: Id9cde46377182ea297962ef8cef89799548e6a77 --- media/libstagefright/Android.bp | 1 + media/libstagefright/codec2/Android.bp | 27 ++++++++++++++ media/libstagefright/codec2/Android.mk | 21 ----------- media/libstagefright/codec2/tests/Android.bp | 31 ++++++++++++++++ media/libstagefright/codec2/tests/Android.mk | 37 -------------------- 5 files changed, 59 insertions(+), 58 deletions(-) create mode 100644 media/libstagefright/codec2/Android.bp delete mode 100644 media/libstagefright/codec2/Android.mk create mode 100644 media/libstagefright/codec2/tests/Android.bp delete mode 100644 media/libstagefright/codec2/tests/Android.mk diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp index 99e6d457d7..db03ff58bd 100644 --- a/media/libstagefright/Android.bp +++ b/media/libstagefright/Android.bp @@ -161,6 +161,7 @@ cc_library_shared { } subdirs = [ + "codec2", "codecs/*", "colorconversion", "filters", diff --git a/media/libstagefright/codec2/Android.bp b/media/libstagefright/codec2/Android.bp new file mode 100644 index 0000000000..e5bc4b39c7 --- /dev/null +++ b/media/libstagefright/codec2/Android.bp @@ -0,0 +1,27 @@ +cc_library_shared { + name: "libstagefright_codec2", + + srcs: ["C2.cpp"], + + include_dirs: [ + "frameworks/av/media/libstagefright/codec2/include", + "frameworks/native/include/media/hardware", + ], + + sanitize: { + misc_undefined: [ + "unsigned-integer-overflow", + "signed-integer-overflow", + ], + cfi: true, + diag: { + cfi: true, + }, + }, + + ldflags: ["-Wl,-Bsymbolic"], +} + +subdirs = [ + "tests", +] diff --git a/media/libstagefright/codec2/Android.mk b/media/libstagefright/codec2/Android.mk deleted file mode 100644 index ef06ed7181..0000000000 --- a/media/libstagefright/codec2/Android.mk +++ /dev/null @@ -1,21 +0,0 @@ -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_SRC_FILES:= \ - C2.cpp \ - -LOCAL_C_INCLUDES += \ - $(TOP)/frameworks/av/media/libstagefright/codec2/include \ - $(TOP)/frameworks/native/include/media/hardware \ - -LOCAL_MODULE:= libstagefright_codec2 -LOCAL_CFLAGS += -Werror -Wall -LOCAL_CLANG := true -LOCAL_SANITIZE := unsigned-integer-overflow signed-integer-overflow cfi -LOCAL_SANITIZE_DIAG := cfi - -include $(BUILD_SHARED_LIBRARY) - -################################################################################ - -include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/media/libstagefright/codec2/tests/Android.bp b/media/libstagefright/codec2/tests/Android.bp new file mode 100644 index 0000000000..1dc6a58c53 --- /dev/null +++ b/media/libstagefright/codec2/tests/Android.bp @@ -0,0 +1,31 @@ +cc_test { + name: "codec2_test", + + tags: [ + "tests", + ], + + srcs: [ + "vndk/C2UtilTest.cpp", + "C2_test.cpp", + "C2Param_test.cpp", + ], + + include_dirs: [ + "frameworks/av/media/libstagefright/codec2/include", + "frameworks/av/media/libstagefright/codec2/vndk/include", + "frameworks/native/include/media/openmax", + ], + + shared_libs: [ + "libcutils", + "liblog", + "libstagefright_codec2", + ], + + cflags: [ + "-Werror", + "-Wall", + "-std=c++14", + ], +} diff --git a/media/libstagefright/codec2/tests/Android.mk b/media/libstagefright/codec2/tests/Android.mk deleted file mode 100644 index 49c4253173..0000000000 --- a/media/libstagefright/codec2/tests/Android.mk +++ /dev/null @@ -1,37 +0,0 @@ -# Build the unit tests. -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) -LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk - -LOCAL_MODULE := codec2_test - -LOCAL_MODULE_TAGS := tests - -LOCAL_SRC_FILES := \ - vndk/C2UtilTest.cpp \ - C2_test.cpp \ - C2Param_test.cpp \ - -LOCAL_SHARED_LIBRARIES := \ - libcutils \ - libstagefright_codec2 \ - liblog - -LOCAL_C_INCLUDES := \ - frameworks/av/media/libstagefright/codec2/include \ - frameworks/av/media/libstagefright/codec2/vndk/include \ - $(TOP)/frameworks/native/include/media/openmax \ - -LOCAL_CFLAGS += -Werror -Wall -std=c++14 -LOCAL_CLANG := true - -include $(BUILD_NATIVE_TEST) - -# Include subdirectory makefiles -# ============================================================ - -# If we're building with ONE_SHOT_MAKEFILE (mm, mmm), then what the framework -# team really wants is to build the stuff defined by this makefile. -ifeq (,$(ONE_SHOT_MAKEFILE)) -include $(call first-makefiles-under,$(LOCAL_PATH)) -endif -- GitLab From f8c34284fcc9b5d64af3199c33cc1cec70a460e2 Mon Sep 17 00:00:00 2001 From: Sanna Catherine de Treville Wager Date: Tue, 25 Jul 2017 11:31:18 -0700 Subject: [PATCH 0015/1203] Write peaks to file. Minor cleanup Test: dumpsys media.log Change-Id: Ie696cd904306997142f050cf842290aeacfb6636 --- media/libnbaio/PerformanceAnalysis.cpp | 26 +++------- media/libnbaio/ReportPerformance.cpp | 52 ++++++++++++------- .../include/media/nbaio/PerformanceAnalysis.h | 9 ++-- .../include/media/nbaio/ReportPerformance.h | 7 +-- 4 files changed, 47 insertions(+), 47 deletions(-) diff --git a/media/libnbaio/PerformanceAnalysis.cpp b/media/libnbaio/PerformanceAnalysis.cpp index d1dff47291..5746222f4e 100644 --- a/media/libnbaio/PerformanceAnalysis.cpp +++ b/media/libnbaio/PerformanceAnalysis.cpp @@ -219,20 +219,6 @@ void PerformanceAnalysis::storeOutlierData(const std::vector ×tamp } } -// FIXME: delete this temporary test code, recycled for various new functions -void PerformanceAnalysis::testFunction() { - // produces values (4: 5000000), (13: 18000000) - // ns timestamps of buffer periods - const std::vectorkTempTestData = {1000000, 4000000, 5000000, - 16000000, 18000000, 28000000}; - PerformanceAnalysis::storeOutlierData(kTempTestData); - for (const auto &outlier: mOutlierData) { - ALOGE("PerformanceAnalysis test %lld: %lld", - static_cast(outlier.first), static_cast(outlier.second)); - } - detectPeaks(); -} - // TODO Make it return a std::string instead of modifying body --> is this still relevant? // TODO consider changing all ints to uint32_t or uint64_t // TODO: move this to ReportPerformance, probably make it a friend function of PerformanceAnalysis @@ -319,15 +305,15 @@ void PerformanceAnalysis::reportPerformance(String8 *body, int maxHeight) { } - +// TODO: decide whether to use this or whether it is overkill, and it is enough +// to only treat as glitches single wakeup call intervals which are too long. +// Ultimately, glitch detection will be directly on the audio signal. // Produces a log warning if the timing of recent buffer periods caused a glitch // Computes sum of running window of three buffer periods // Checks whether the buffer periods leave enough CPU time for the next one // e.g. if a buffer period is expected to be 4 ms and a buffer requires 3 ms of CPU time, // here are some glitch cases: // 4 + 4 + 6 ; 5 + 4 + 5; 2 + 2 + 10 -// TODO: develop this code to track changes in histogram distribution in addition -// to / instead of glitches. void PerformanceAnalysis::alertIfGlitch(const std::vector &samples) { std::deque periods(kNumBuff, kPeriodMs); for (size_t i = 2; i < samples.size(); ++i) { // skip first time entry @@ -348,7 +334,7 @@ void PerformanceAnalysis::alertIfGlitch(const std::vector &samples) { // writes summary of performance into specified file descriptor void dump(int fd, int indent, PerformanceAnalysisMap &threadPerformanceAnalysis) { String8 body; - const char* const kName = "/data/misc/audioserver/"; + const char* const kDirectory = "/data/misc/audioserver/"; for (auto & thread : threadPerformanceAnalysis) { for (auto & hash: thread.second) { PerformanceAnalysis& curr = hash.second; @@ -356,8 +342,8 @@ void dump(int fd, int indent, PerformanceAnalysisMap &threadPerformanceAnalysis) // write performance data to console curr.reportPerformance(&body); // write to file - writeToFile(curr.mOutlierData, curr.mHists, kName, false, - thread.first, hash.first); + writeToFile(curr.mHists, curr.mOutlierData, curr.mPeakTimestamps, + kDirectory, false, thread.first, hash.first); } } if (!body.isEmpty()) { diff --git a/media/libnbaio/ReportPerformance.cpp b/media/libnbaio/ReportPerformance.cpp index 87c223bfec..fa2b9a0778 100644 --- a/media/libnbaio/ReportPerformance.cpp +++ b/media/libnbaio/ReportPerformance.cpp @@ -38,10 +38,10 @@ namespace ReportPerformance { // Writes outlier intervals, timestamps, and histograms spanning long time intervals to a file. // TODO: format the data efficiently and write different types of data to different files -void writeToFile(const std::deque> &outlierData, - const std::deque> &hists, - const char * kDirectory, - bool append, int author, log_hash_t hash) { +void writeToFile(const std::deque> &hists, + const std::deque> &outlierData, + const std::deque &peakTimestamps, + const char * directory, bool append, int author, log_hash_t hash) { if (outlierData.empty() || hists.empty()) { ALOGW("No data, returning."); return; @@ -49,24 +49,14 @@ void writeToFile(const std::deque> &outlie std::stringstream outlierName; std::stringstream histogramName; + std::stringstream peakName; - outlierName << kDirectory << "outliers_" << author << "_" << hash; - histogramName << kDirectory << "histograms_" << author << "_" << hash; - - std::ofstream ofs; - ofs.open(outlierName.str().c_str(), append ? std::ios::app : std::ios::trunc); - if (!ofs.is_open()) { - ALOGW("couldn't open file %s", outlierName.str().c_str()); - return; - } - ofs << "Outlier data: interval and timestamp\n"; - for (const auto &outlier : outlierData) { - ofs << outlier.first << ": " << outlier.second << "\n"; - } - ofs.close(); + histogramName << directory << "histograms_" << author << "_" << hash; + outlierName << directory << "outliers_" << author << "_" << hash; + peakName << directory << "peaks_" << author << "_" << hash; std::ofstream hfs; - hfs.open(histogramName.str().c_str(), append ? std::ios::app : std::ios::trunc); + hfs.open(histogramName.str(), append ? std::ios::app : std::ios::trunc); if (!hfs.is_open()) { ALOGW("couldn't open file %s", histogramName.str().c_str()); return; @@ -82,6 +72,30 @@ void writeToFile(const std::deque> &outlie hfs << "\n"; // separate histograms with a newline } hfs.close(); + + std::ofstream ofs; + ofs.open(outlierName.str(), append ? std::ios::app : std::ios::trunc); + if (!ofs.is_open()) { + ALOGW("couldn't open file %s", outlierName.str().c_str()); + return; + } + ofs << "Outlier data: interval and timestamp\n"; + for (const auto &outlier : outlierData) { + ofs << outlier.first << ": " << outlier.second << "\n"; + } + ofs.close(); + + std::ofstream pfs; + pfs.open(peakName.str(), append ? std::ios::app : std::ios::trunc); + if (!pfs.is_open()) { + ALOGW("couldn't open file %s", peakName.str().c_str()); + return; + } + pfs << "Peak data: timestamp\n"; + for (const auto &peak : peakTimestamps) { + pfs << peak << "\n"; + } + pfs.close(); } } // namespace ReportPerformance diff --git a/media/libnbaio/include/media/nbaio/PerformanceAnalysis.h b/media/libnbaio/include/media/nbaio/PerformanceAnalysis.h index da68821668..dc6a989210 100644 --- a/media/libnbaio/include/media/nbaio/PerformanceAnalysis.h +++ b/media/libnbaio/include/media/nbaio/PerformanceAnalysis.h @@ -79,11 +79,10 @@ public: // FIXME: move this data visualization to a separate class. Model/view/controller void reportPerformance(String8 *body, int maxHeight = 10); - // TODO: delete this. temp for testing - void testFunction(); - - // This function used to detect glitches in a time series - // TODO incorporate this into the analysis (currently unused) + // This function detects glitches in a time series. + // TODO: decide whether to use this or whether it is overkill, and it is enough + // to only treat as glitches single wakeup call intervals which are too long. + // Ultimately, glitch detection will be directly on the audio signal. void alertIfGlitch(const std::vector &samples); private: diff --git a/media/libnbaio/include/media/nbaio/ReportPerformance.h b/media/libnbaio/include/media/nbaio/ReportPerformance.h index afbbb75a58..bfab470ac3 100644 --- a/media/libnbaio/include/media/nbaio/ReportPerformance.h +++ b/media/libnbaio/include/media/nbaio/ReportPerformance.h @@ -56,9 +56,10 @@ static inline uint32_t log2(uint32_t x) { // Writes outlier intervals, timestamps, and histograms spanning long time // intervals to a file. -void writeToFile(const std::deque> &outlierData, - const std::deque> &hists, - const char * kName, bool append, int author, log_hash_t hash); +void writeToFile(const std::deque> &hists, + const std::deque> &outlierData, + const std::deque &peakTimestamps, + const char * kDirectory, bool append, int author, log_hash_t hash); } // namespace ReportPerformance -- GitLab From 716d2c6660d579301804b44c9b6ec8a3a5d9a50a Mon Sep 17 00:00:00 2001 From: Sanna Catherine de Treville Wager Date: Tue, 25 Jul 2017 13:46:36 -0700 Subject: [PATCH 0016/1203] Organize PerformanceAnalysis members in structs Stores related variables in structs instead of directly in the class definition Test: dumpsys media.log Change-Id: I776e35e74ac69b428170b379a9bcf4d18b3fa041 --- media/libnbaio/PerformanceAnalysis.cpp | 66 ++++++++++--------- .../include/media/nbaio/PerformanceAnalysis.h | 37 ++++++----- .../include/media/nbaio/ReportPerformance.h | 2 + 3 files changed, 57 insertions(+), 48 deletions(-) diff --git a/media/libnbaio/PerformanceAnalysis.cpp b/media/libnbaio/PerformanceAnalysis.cpp index 5746222f4e..7f4548f1d6 100644 --- a/media/libnbaio/PerformanceAnalysis.cpp +++ b/media/libnbaio/PerformanceAnalysis.cpp @@ -87,12 +87,12 @@ void PerformanceAnalysis::processAndFlushTimeStampSeries() { // if the current histogram has spanned its maximum time interval, // insert a new empty histogram to the front of mHists - if (deltaMs(mHists[0].first, mTimeStampSeries[0]) >= kMaxHistTimespanMs) { + if (deltaMs(mHists[0].first, mTimeStampSeries[0]) >= kMaxLength.HistTimespanMs) { mHists.emplace_front(static_cast(mTimeStampSeries[0]), std::map()); // When memory is full, delete oldest histogram - if (mHists.size() >= kHistsCapacity) { - mHists.resize(kHistsCapacity); + if (mHists.size() >= kMaxLength.Hists) { + mHists.resize(kMaxLength.Hists); } } @@ -125,7 +125,7 @@ void PerformanceAnalysis::logTsEntry(int64_t ts) { mTimeStampSeries.push_back(ts); // if length of the time series has reached kShortHistSize samples, // analyze the data and flush the timestamp series from memory - if (mTimeStampSeries.size() >= kHistSize) { + if (mTimeStampSeries.size() >= kMaxLength.TimeStamps) { processAndFlushTimeStampSeries(); } } @@ -152,36 +152,39 @@ void PerformanceAnalysis::detectPeaks() { // the mean and standard deviation are updated every time a peak is detected // initialize first time. The mean from the previous sequence is stored // for the next sequence. Here, they are initialized for the first time. - if (mPeakDetectorMean < 0) { - mPeakDetectorMean = static_cast(start->first); - mPeakDetectorSd = 0; + if (mOutlierDistribution.Mean < 0) { + mOutlierDistribution.Mean = static_cast(start->first); + mOutlierDistribution.Sd = 0; } auto sqr = [](auto x){ return x * x; }; for (auto it = mOutlierData.begin(); it != mOutlierData.end(); ++it) { // no surprise occurred: // the new element is a small number of standard deviations from the mean - if ((fabs(it->first - mPeakDetectorMean) < kStddevThreshold * mPeakDetectorSd) || + if ((fabs(it->first - mOutlierDistribution.Mean) < + mOutlierDistribution.kMaxDeviation * mOutlierDistribution.Sd) || // or: right after peak has been detected, the delta is smaller than average - (mPeakDetectorSd == 0 && fabs(it->first - mPeakDetectorMean) < kTypicalDiff)) { + (mOutlierDistribution.Sd == 0 && + fabs(it->first - mOutlierDistribution.Mean) < kTypicalDiff)) { // update the mean and sd: // count number of elements (distance between start interator and current) const int kN = std::distance(start, it) + 1; // usual formulas for mean and sd - mPeakDetectorMean = std::accumulate(start, it + 1, 0.0, + mOutlierDistribution.Mean = std::accumulate(start, it + 1, 0.0, [](auto &a, auto &b){return a + b.first;}) / kN; - mPeakDetectorSd = sqrt(std::accumulate(start, it + 1, 0.0, - [=](auto &a, auto &b){ return a + sqr(b.first - mPeakDetectorMean);})) / - ((kN > 1)? kN - 1 : kN); // kN - 1: mean is correlated with variance + mOutlierDistribution.Sd = sqrt(std::accumulate(start, it + 1, 0.0, + [=](auto &a, auto &b){ + return a + sqr(b.first - mOutlierDistribution.Mean);})) / + ((kN > 1)? kN - 1 : kN); // kN - 1: mean is correlated with variance } // surprising value: store peak timestamp and reset mean, sd, and start iterator else { - mPeakTimestamps.emplace_back(it->second); - // TODO: remove pop_front once a circular buffer is in place - if (mPeakTimestamps.size() >= kPeakSeriesSize) { - mPeakTimestamps.pop_front(); + mPeakTimestamps.emplace_front(it->second); + // TODO: turn this into a circular buffer + if (mPeakTimestamps.size() >= kMaxLength.Peaks) { + mPeakTimestamps.resize(kMaxLength.Peaks); } - mPeakDetectorMean = static_cast(it->first); - mPeakDetectorSd = 0; + mOutlierDistribution.Mean = static_cast(it->first); + mOutlierDistribution.Sd = 0; start = it; } } @@ -190,7 +193,8 @@ void PerformanceAnalysis::detectPeaks() { // Called by LogTsEntry. The input is a vector of timestamps. // Finds outliers and writes to mOutlierdata. -// Each value in mOutlierdata consists of: . +// Each value in mOutlierdata consists of: . // e.g. timestamps (ms) 1, 4, 5, 16, 18, 28 will produce pairs (4, 5), (13, 18). // This function is applied to the time series before it is converted into a histogram. void PerformanceAnalysis::storeOutlierData(const std::vector ×tamps) { @@ -198,24 +202,25 @@ void PerformanceAnalysis::storeOutlierData(const std::vector ×tamp return; } // first pass: need to initialize - if (mElapsed == 0) { - mPrevNs = timestamps[0]; + if (mOutlierDistribution.Elapsed == 0) { + mOutlierDistribution.PrevNs = timestamps[0]; } for (const auto &ts: timestamps) { - const uint64_t diffMs = static_cast(deltaMs(mPrevNs, ts)); + const uint64_t diffMs = static_cast(deltaMs(mOutlierDistribution.PrevNs, ts)); if (diffMs >= static_cast(kOutlierMs)) { - mOutlierData.emplace_back(mElapsed, static_cast(mPrevNs)); + mOutlierData.emplace_front(mOutlierDistribution.Elapsed, + static_cast(mOutlierDistribution.PrevNs)); // Remove oldest value if the vector is full // TODO: remove pop_front once circular buffer is in place // FIXME: make sure kShortHistSize is large enough that that data will never be lost // before being written to file or to a FIFO - if (mOutlierData.size() >= kOutlierSeriesSize) { - mOutlierData.pop_front(); + if (mOutlierData.size() >= kMaxLength.Outliers) { + mOutlierData.resize(kMaxLength.Outliers); } - mElapsed = 0; + mOutlierDistribution.Elapsed = 0; } - mElapsed += diffMs; - mPrevNs = ts; + mOutlierDistribution.Elapsed += diffMs; + mOutlierDistribution.PrevNs = ts; } } @@ -279,7 +284,8 @@ void PerformanceAnalysis::reportPerformance(String8 *body, int maxHeight) { const int value = 1 << row; body->appendFormat("%.*s", leftPadding, spaces.c_str()); for (auto const &x : buckets) { - body->appendFormat("%.*s%s", colWidth - 1, spaces.c_str(), x.second < value ? " " : "|"); + body->appendFormat("%.*s%s", colWidth - 1, + spaces.c_str(), x.second < value ? " " : "|"); } body->appendFormat("\n%s", " "); } diff --git a/media/libnbaio/include/media/nbaio/PerformanceAnalysis.h b/media/libnbaio/include/media/nbaio/PerformanceAnalysis.h index dc6a989210..81f9c582be 100644 --- a/media/libnbaio/include/media/nbaio/PerformanceAnalysis.h +++ b/media/libnbaio/include/media/nbaio/PerformanceAnalysis.h @@ -102,10 +102,9 @@ private: // when a vector reaches its maximum size, the data is processed and flushed std::vector mTimeStampSeries; - static const int kMsPerSec = 1000; - // Parameters used when detecting outliers // TODO: learn some of these from the data, delete unused ones + // TODO: put used variables in a struct // FIXME: decide whether to make kPeriodMs static. static const int kNumBuff = 3; // number of buffers considered in local history int kPeriodMs; // current period length is ideally 4 ms @@ -114,27 +113,29 @@ private: static constexpr double kRatio = 0.75; // estimate of CPU time as ratio of period length int kPeriodMsCPU; // compute based on kPeriodLen and kRatio - // Peak detection: number of standard deviations from mean considered a significant change - static const int kStddevThreshold = 5; - // capacity allocated to data structures // TODO: make these values longer when testing is finished - static const int kHistsCapacity = 20; // number of short-term histograms stored in memory - static const int kHistSize = 1000; // max number of samples stored in a histogram - static const int kOutlierSeriesSize = 100; // number of values stored in outlier array - static const int kPeakSeriesSize = 100; // number of values stored in peak array - // maximum elapsed time between first and last timestamp of a long-term histogram - static const int kMaxHistTimespanMs = 5 * kMsPerSec; + struct MaxLength { + size_t Hists; // number of histograms stored in memory + size_t TimeStamps; // histogram size, e.g. maximum length of timestamp series + size_t Outliers; // number of values stored in outlier array + size_t Peaks; // number of values stored in peak array + // maximum elapsed time between first and last timestamp of a long-term histogram + int HistTimespanMs; + }; + static constexpr MaxLength kMaxLength = {.Hists = 20, .TimeStamps = 1000, + .Outliers = 100, .Peaks = 100, .HistTimespanMs = 5 * kMsPerSec }; // these variables are stored in-class to ensure continuity while analyzing the timestamp // series one short sequence at a time: the variables are not re-initialized every time. - // FIXME: create inner class for these variables and decide which other ones to add to it - double mPeakDetectorMean = -1; - double mPeakDetectorSd = -1; - // variables for storeOutlierData - uint64_t mElapsed = 0; - int64_t mPrevNs = -1; - + struct OutlierDistribution { + double Mean = -1; + double Sd = -1; + uint64_t Elapsed = 0; + int64_t PrevNs = -1; + // number of standard deviations from mean considered a significant change + const int kMaxDeviation = 5; + } mOutlierDistribution; }; void dump(int fd, int indent, PerformanceAnalysisMap &threadPerformanceAnalysis); diff --git a/media/libnbaio/include/media/nbaio/ReportPerformance.h b/media/libnbaio/include/media/nbaio/ReportPerformance.h index bfab470ac3..ed97a23991 100644 --- a/media/libnbaio/include/media/nbaio/ReportPerformance.h +++ b/media/libnbaio/include/media/nbaio/ReportPerformance.h @@ -29,6 +29,8 @@ class String8; namespace ReportPerformance { +const int kMsPerSec = 1000; + // stores a histogram: key: observed buffer period. value: count // TODO: unsigned, unsigned using Histogram = std::map; -- GitLab From cd5a09d650548ae999c1da2fb5256a459289f403 Mon Sep 17 00:00:00 2001 From: Ian Elliott Date: Wed, 26 Jul 2017 21:46:42 +0000 Subject: [PATCH 0017/1203] Revert "Have the Surface class track the buffer age." This reverts commit e0ecc35b14f581ab7adf1c131fd6de24c9f8dbb5. Change-Id: I5c42683f9eadec78138eca0f5541d9d161e5bec9 --- media/libstagefright/omx/1.0/WGraphicBufferProducer.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/media/libstagefright/omx/1.0/WGraphicBufferProducer.cpp b/media/libstagefright/omx/1.0/WGraphicBufferProducer.cpp index c11c986700..acda060133 100644 --- a/media/libstagefright/omx/1.0/WGraphicBufferProducer.cpp +++ b/media/libstagefright/omx/1.0/WGraphicBufferProducer.cpp @@ -64,9 +64,10 @@ Return TWGraphicBufferProducer::dequeueBuffer( sp fence; ::android::FrameEventHistoryDelta outTimestamps; status_t status = mBase->dequeueBuffer( - &slot, &fence, width, height, - static_cast<::android::PixelFormat>(format), usage, nullptr, - getFrameTimestamps ? &outTimestamps : nullptr); + &slot, &fence, + width, height, + static_cast<::android::PixelFormat>(format), usage, + getFrameTimestamps ? &outTimestamps : nullptr); hidl_handle tFence; FrameEventHistoryDelta tOutTimestamps; -- GitLab From ee9ddeff468484061c5c8df1368867cd31ec09d5 Mon Sep 17 00:00:00 2001 From: Ian Elliott Date: Tue, 18 Jul 2017 15:53:06 -0600 Subject: [PATCH 0018/1203] Have the Surface class track the buffer age. This change corresponds to a change between the Surface and BufferQueueProducer classes. Have the Surface class track the buffer age, so that Surface::query() can return the buffer age without having to use a binder call to BufferQueueProducer::query(). The idea is for BufferQueueProducer::dequeueBuffer() to return the value, which the Surface class will cache for later use by Surface::query(). Bug: b/27903668 Test: Use systrace to no ensure query binder call after dequeueBuffer. Change-Id: I78ff3d2d639111705c25a92f3672b7e6d0fac19f --- media/libstagefright/omx/1.0/WGraphicBufferProducer.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/media/libstagefright/omx/1.0/WGraphicBufferProducer.cpp b/media/libstagefright/omx/1.0/WGraphicBufferProducer.cpp index d2b245454b..fcf1092d4b 100644 --- a/media/libstagefright/omx/1.0/WGraphicBufferProducer.cpp +++ b/media/libstagefright/omx/1.0/WGraphicBufferProducer.cpp @@ -64,10 +64,9 @@ Return TWGraphicBufferProducer::dequeueBuffer( sp fence; ::android::FrameEventHistoryDelta outTimestamps; status_t status = mBase->dequeueBuffer( - &slot, &fence, - width, height, - static_cast<::android::PixelFormat>(format), usage, - getFrameTimestamps ? &outTimestamps : nullptr); + &slot, &fence, width, height, + static_cast<::android::PixelFormat>(format), usage, nullptr, + getFrameTimestamps ? &outTimestamps : nullptr); hidl_handle tFence; FrameEventHistoryDelta tOutTimestamps; -- GitLab From 39b159bbf6c736a16ef6c07c09b605deefaafa4a Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Tue, 14 Feb 2017 11:06:25 +0900 Subject: [PATCH 0019/1203] codec2: misc fixes - Change C2ComponentInterface::getSupportedValues method signautre. - Define C2VideoSizeStruct with no base. - Add C2Param::Copy() Test: codec2_test Change-Id: I16c872e240b2bfe1d72d154225682a1bff731c4a --- .../codec2/include/C2Component.h | 2 +- .../libstagefright/codec2/include/C2Config.h | 12 ++++++--- media/libstagefright/codec2/include/C2Param.h | 11 ++++++++ .../codec2/tests/C2Param_test.cpp | 27 +++++++++++++++++-- 4 files changed, 45 insertions(+), 7 deletions(-) diff --git a/media/libstagefright/codec2/include/C2Component.h b/media/libstagefright/codec2/include/C2Component.h index 1ee9302e7c..3e93d41a8e 100644 --- a/media/libstagefright/codec2/include/C2Component.h +++ b/media/libstagefright/codec2/include/C2Component.h @@ -282,7 +282,7 @@ public: * fields in the same list? */ virtual status_t getSupportedValues( - const std::vector fields, + const std::vector &fields, std::vector* const values) const = 0; virtual ~C2ComponentInterface() = default; diff --git a/media/libstagefright/codec2/include/C2Config.h b/media/libstagefright/codec2/include/C2Config.h index 30e9193a25..18e0a47846 100644 --- a/media/libstagefright/codec2/include/C2Config.h +++ b/media/libstagefright/codec2/include/C2Config.h @@ -67,6 +67,7 @@ enum C2ParamIndexKind : uint32_t { kParamIndexStructStart = 0x1, kParamIndexVideoSize, kParamIndexMaxVideoSizeHint, + kParamIndexVideoSizeTuning, kParamIndexParamStart = 0x800, }; @@ -230,19 +231,22 @@ struct C2VideoSizeStruct { int32_t mWidth; ///< video width int32_t mHeight; ///< video height - DEFINE_AND_DESCRIBE_C2STRUCT(VideoSize) + DEFINE_C2STRUCT_NO_BASE(VideoSize) +} C2_PACK; + +DESCRIBE_C2STRUCT(VideoSize, { C2FIELD(mWidth, "width") C2FIELD(mHeight, "height") -}; +}) // video size for video decoder [OUT] -typedef C2StreamParam C2VideoSizeStreamInfo; +typedef C2StreamParam C2VideoSizeStreamInfo; // max video size for video decoder [IN] typedef C2PortParam C2MaxVideoSizeHintPortSetting; // video encoder size [IN] -typedef C2StreamParam C2VideoSizeStreamTuning; +typedef C2StreamParam C2VideoSizeStreamTuning; /// @} diff --git a/media/libstagefright/codec2/include/C2Param.h b/media/libstagefright/codec2/include/C2Param.h index fd43061944..dec06ae5ff 100644 --- a/media/libstagefright/codec2/include/C2Param.h +++ b/media/libstagefright/codec2/include/C2Param.h @@ -362,6 +362,17 @@ public: return param; } + /// Returns managed clone of |orig| at heap. + inline static std::unique_ptr Copy(const C2Param &orig) { + if (orig.size() == 0) { + return nullptr; + } + void *mem = ::operator new (orig.size()); + C2Param *param = new (mem) C2Param(orig.size(), orig._mIndex); + param->updateFrom(orig); + return std::unique_ptr(param); + } + #if 0 template P *As() { return P::From(this); } diff --git a/media/libstagefright/codec2/tests/C2Param_test.cpp b/media/libstagefright/codec2/tests/C2Param_test.cpp index ec82c84a7b..9165aad630 100644 --- a/media/libstagefright/codec2/tests/C2Param_test.cpp +++ b/media/libstagefright/codec2/tests/C2Param_test.cpp @@ -968,6 +968,10 @@ TEST_F(C2ParamTest, ParamOpsTest) { EXPECT_EQ(C2NumberStreamTuning::From(&tun), nullptr); EXPECT_EQ(C2NumberStreamTuning::input::From(&tun), nullptr); EXPECT_EQ(C2NumberStreamTuning::output::From(&tun), nullptr); + + EXPECT_EQ(*(C2Param::Copy(btun)), btun); + btun.invalidate(); + EXPECT_FALSE(C2Param::Copy(btun)); } const C2NumberPortTuning outp1(true, 100), inp1(false, 100); @@ -1171,6 +1175,11 @@ TEST_F(C2ParamTest, ParamOpsTest) { EXPECT_EQ(C2NumberStreamTuning::output::From(&inp2), nullptr); EXPECT_EQ(C2NumberStreamTuning::output::From(&outp1), nullptr); EXPECT_EQ(C2NumberStreamTuning::output::From(&outp2), nullptr); + + EXPECT_EQ(*(C2Param::Copy(inp1)), inp1); + EXPECT_EQ(*(C2Param::Copy(inp2)), inp2); + EXPECT_EQ(*(C2Param::Copy(outp1)), outp1); + EXPECT_EQ(*(C2Param::Copy(outp2)), outp2); } const C2NumberStreamTuning outs1(true, 1u, 100), ins1(false, 1u, 100); @@ -1383,6 +1392,10 @@ TEST_F(C2ParamTest, ParamOpsTest) { EXPECT_EQ(C2NumberStreamTuning::output::From(&outs1), (C2NumberStreamTuning::output*)&outs1); EXPECT_EQ(C2NumberStreamTuning::output::From(&outs2), &outs2); + EXPECT_EQ(*(C2Param::Copy(ins1)), ins1); + EXPECT_EQ(*(C2Param::Copy(ins2)), ins2); + EXPECT_EQ(*(C2Param::Copy(outs1)), outs1); + EXPECT_EQ(*(C2Param::Copy(outs2)), outs2); } { @@ -1518,6 +1531,8 @@ TEST_F(C2ParamTest, FlexParamOpsTest) { EXPECT_EQ(C2NumbersStreamTuning::From(tun.get()), nullptr); EXPECT_EQ(C2NumbersStreamTuning::input::From(tun.get()), nullptr); EXPECT_EQ(C2NumbersStreamTuning::output::From(tun.get()), nullptr); + + EXPECT_EQ(*(C2Param::Copy(*tun)), *tun); } std::unique_ptr outp1_(C2NumbersPortTuning::alloc_unique(1, true)), @@ -1739,6 +1754,10 @@ TEST_F(C2ParamTest, FlexParamOpsTest) { EXPECT_EQ(C2NumbersStreamTuning::output::From(outp1.get()), nullptr); EXPECT_EQ(C2NumbersStreamTuning::output::From(outp2.get()), nullptr); + EXPECT_EQ(*(C2Param::Copy(*inp1)), *inp1); + EXPECT_EQ(*(C2Param::Copy(*inp2)), *inp2); + EXPECT_EQ(*(C2Param::Copy(*outp1)), *outp1); + EXPECT_EQ(*(C2Param::Copy(*outp2)), *outp2); } std::unique_ptr outs1_(C2NumbersStreamTuning::alloc_unique(1, true, 1u)); @@ -1968,6 +1987,10 @@ TEST_F(C2ParamTest, FlexParamOpsTest) { EXPECT_EQ(C2NumbersStreamTuning::output::From(outs1.get()), (C2NumbersStreamTuning::output*)outs1.get()); EXPECT_EQ(C2NumbersStreamTuning::output::From(outs2.get()), outs2.get()); + EXPECT_EQ(*(C2Param::Copy(*ins1)), *ins1); + EXPECT_EQ(*(C2Param::Copy(*ins2)), *ins2); + EXPECT_EQ(*(C2Param::Copy(*outs1)), *outs1); + EXPECT_EQ(*(C2Param::Copy(*outs2)), *outs2); } { @@ -2262,7 +2285,7 @@ public: for (const C2Param::Index index : heapParamIndices) { if (mMyParams.count(index)) { C2Param & myParam = mMyParams.find(index)->second; - std::unique_ptr paramCopy(C2Param::From(&myParam, myParam.size())); + std::unique_ptr paramCopy(C2Param::Copy(myParam)); heapParams->push_back(std::move(paramCopy)); } } @@ -2303,7 +2326,7 @@ public: }; virtual status_t getSupportedValues( - const std::vector fields, + const std::vector &fields, std::vector* const values) const { for (const C2ParamField &field : fields) { if (field == C2ParamField(&mDomainInfo, &C2ComponentDomainInfo::mValue)) { -- GitLab From 0bbe5fe6b5c123716afa83efa99c7a8015a4b8d5 Mon Sep 17 00:00:00 2001 From: Lajos Molnar Date: Fri, 29 Apr 2016 21:42:18 -0700 Subject: [PATCH 0020/1203] stagefright: Codec2 VNDK 1D Initial draft implementation of some Codec2 concepts: ion-based allocation, param utilities Test: codec2_test Bug: 30262321 Change-Id: I126d1864eac176cb4fc0b9e1a7cab8ecc9aa9121 --- media/libstagefright/codec2/Android.bp | 5 + .../libstagefright/codec2/include/C2Buffer.h | 32 +- media/libstagefright/codec2/include/C2Param.h | 10 +- media/libstagefright/codec2/tests/Android.bp | 11 + .../codec2/tests/vndk/C2BufferTest.cpp | 164 ++++ media/libstagefright/codec2/vndk/Android.bp | 30 + media/libstagefright/codec2/vndk/C2Buffer.cpp | 720 ++++++++++++++++++ .../codec2/vndk/include/C2BufferPriv.h | 84 ++ 8 files changed, 1048 insertions(+), 8 deletions(-) create mode 100644 media/libstagefright/codec2/tests/vndk/C2BufferTest.cpp create mode 100644 media/libstagefright/codec2/vndk/Android.bp create mode 100644 media/libstagefright/codec2/vndk/C2Buffer.cpp create mode 100644 media/libstagefright/codec2/vndk/include/C2BufferPriv.h diff --git a/media/libstagefright/codec2/Android.bp b/media/libstagefright/codec2/Android.bp index e5bc4b39c7..f79e05872b 100644 --- a/media/libstagefright/codec2/Android.bp +++ b/media/libstagefright/codec2/Android.bp @@ -1,6 +1,10 @@ cc_library_shared { name: "libstagefright_codec2", + tags: [ + "optional", + ], + srcs: ["C2.cpp"], include_dirs: [ @@ -24,4 +28,5 @@ cc_library_shared { subdirs = [ "tests", + "vndk", ] diff --git a/media/libstagefright/codec2/include/C2Buffer.h b/media/libstagefright/codec2/include/C2Buffer.h index 9f6b487ac4..88f1db3cc2 100644 --- a/media/libstagefright/codec2/include/C2Buffer.h +++ b/media/libstagefright/codec2/include/C2Buffer.h @@ -223,7 +223,11 @@ public: * * \return acquired object potentially invalidated if waiting for the fence failed. */ - T get(); + T get() { + // TODO: + // wait(); + return mT; + } protected: C2Acquirable(C2Error error, C2Fence fence, T t) : C2Fence(fence), mInitialError(error), mT(t) { } @@ -268,7 +272,7 @@ protected: : mCapacity(parent == nullptr ? 0 : parent->capacity()) { } private: - const uint32_t mCapacity; + uint32_t mCapacity; /// @} }; @@ -429,7 +433,7 @@ public: /** * \return pointer to the start of the block or nullptr on error. */ - const uint8_t *data(); + const uint8_t *data() const; /** * Returns a portion of this view. @@ -447,6 +451,10 @@ public: */ C2Error error(); +protected: + C2ReadView(const _C2LinearCapacityAspect *parent, const uint8_t *data); + explicit C2ReadView(C2Error error); + private: class Impl; std::shared_ptr mImpl; @@ -476,6 +484,10 @@ public: */ C2Error error(); +protected: + C2WriteView(const _C2LinearRangeAspect *parent, uint8_t *base); + explicit C2WriteView(C2Error error); + private: class Impl; /// \todo should this be unique_ptr to make this movable only - to avoid inconsistent regions @@ -516,7 +528,13 @@ public: */ C2Fence fence() const { return mFence; } +protected: + C2ConstLinearBlock(std::shared_ptr alloc); + C2ConstLinearBlock(std::shared_ptr alloc, size_t offset, size_t size); + private: + class Impl; + std::shared_ptr mImpl; C2Fence mFence; }; @@ -544,6 +562,14 @@ public: * The block shall be modified only until firing the event for the fence. */ C2ConstLinearBlock share(size_t offset, size_t size, C2Fence fence); + +protected: + C2LinearBlock(std::shared_ptr alloc); + C2LinearBlock(std::shared_ptr alloc, size_t offset, size_t size); + +private: + class Impl; + std::shared_ptr mImpl; }; /// @} diff --git a/media/libstagefright/codec2/include/C2Param.h b/media/libstagefright/codec2/include/C2Param.h index dec06ae5ff..aab0474e5d 100644 --- a/media/libstagefright/codec2/include/C2Param.h +++ b/media/libstagefright/codec2/include/C2Param.h @@ -672,11 +672,11 @@ private: Primitive mValue; }; -template<> const int32_t &C2Value::Primitive::ref() const { return i32; } -template<> const int64_t &C2Value::Primitive::ref() const { return i64; } -template<> const uint32_t &C2Value::Primitive::ref() const { return u32; } -template<> const uint64_t &C2Value::Primitive::ref() const { return u64; } -template<> const float &C2Value::Primitive::ref() const { return fp; } +template<> inline const int32_t &C2Value::Primitive::ref() const { return i32; } +template<> inline const int64_t &C2Value::Primitive::ref() const { return i64; } +template<> inline const uint32_t &C2Value::Primitive::ref() const { return u32; } +template<> inline const uint64_t &C2Value::Primitive::ref() const { return u64; } +template<> inline const float &C2Value::Primitive::ref() const { return fp; } template<> constexpr C2Value::Type C2Value::typeFor() { return INT32; } template<> constexpr C2Value::Type C2Value::typeFor() { return INT64; } diff --git a/media/libstagefright/codec2/tests/Android.bp b/media/libstagefright/codec2/tests/Android.bp index 1dc6a58c53..a8a6565d0a 100644 --- a/media/libstagefright/codec2/tests/Android.bp +++ b/media/libstagefright/codec2/tests/Android.bp @@ -6,6 +6,7 @@ cc_test { ], srcs: [ + "vndk/C2BufferTest.cpp", "vndk/C2UtilTest.cpp", "C2_test.cpp", "C2Param_test.cpp", @@ -21,6 +22,16 @@ cc_test { "libcutils", "liblog", "libstagefright_codec2", + "libcutils", + "libhidlbase", + "libion", + "liblog", + "libstagefright_codec2", + "libutils", + ], + + static_libs: [ + "libstagefright_codec2_vndk", ], cflags: [ diff --git a/media/libstagefright/codec2/tests/vndk/C2BufferTest.cpp b/media/libstagefright/codec2/tests/vndk/C2BufferTest.cpp new file mode 100644 index 0000000000..0ba3cad1d5 --- /dev/null +++ b/media/libstagefright/codec2/tests/vndk/C2BufferTest.cpp @@ -0,0 +1,164 @@ +/* + * Copyright 2017 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 + +#include +#include + +#include + +namespace android { + +class C2BufferTest : public ::testing::Test { +public: + C2BufferTest() + : mAllocator(std::make_shared()), + mSize(0u), + mAddr(nullptr) { + } + + ~C2BufferTest() = default; + + void allocate(size_t capacity) { + C2Error err = mAllocator->allocateLinearBuffer( + capacity, + { C2MemoryUsage::kSoftwareRead, C2MemoryUsage::kSoftwareWrite }, + &mAllocation); + if (err != C2_OK) { + mAllocation.reset(); + FAIL() << "C2Allocator::allocateLinearBuffer() failed: " << err; + } + } + + void map(size_t offset, size_t size, uint8_t **addr) { + ASSERT_TRUE(mAllocation); + C2Error err = mAllocation->map( + offset, + size, + { C2MemoryUsage::kSoftwareRead, C2MemoryUsage::kSoftwareWrite }, + // TODO: fence + nullptr, + &mAddr); + if (err != C2_OK) { + mAddr = nullptr; + FAIL() << "C2LinearAllocation::map() failed: " << err; + } + ASSERT_NE(nullptr, mAddr); + mSize = size; + *addr = (uint8_t *)mAddr; + } + + void unmap() { + ASSERT_TRUE(mAllocation); + ASSERT_NE(nullptr, mAddr); + ASSERT_NE(0u, mSize); + + // TODO: fence + ASSERT_EQ(C2_OK, mAllocation->unmap(mAddr, mSize, nullptr)); + mSize = 0u; + mAddr = nullptr; + } + + std::shared_ptr makeBlockAllocator() { + return std::make_shared(mAllocator); + } + +private: + std::shared_ptr mAllocator; + std::shared_ptr mAllocation; + size_t mSize; + void *mAddr; +}; + +TEST_F(C2BufferTest, LinearAllocationTest) { + constexpr size_t kCapacity = 1024u * 1024u; + + allocate(kCapacity); + + uint8_t *addr = nullptr; + map(0u, kCapacity, &addr); + ASSERT_NE(nullptr, addr); + + for (size_t i = 0; i < kCapacity; ++i) { + addr[i] = i % 100u; + } + + unmap(); + addr = nullptr; + + map(kCapacity / 3, kCapacity / 3, &addr); + ASSERT_NE(nullptr, addr); + for (size_t i = 0; i < kCapacity / 3; ++i) { + ASSERT_EQ((i + kCapacity / 3) % 100, addr[i]) << " at i = " << i; + } +} + +TEST_F(C2BufferTest, BlockAllocatorTest) { + constexpr size_t kCapacity = 1024u * 1024u; + + std::shared_ptr blockAllocator(makeBlockAllocator()); + + std::shared_ptr block; + ASSERT_EQ(C2_OK, blockAllocator->allocateLinearBlock( + kCapacity, + { C2MemoryUsage::kSoftwareRead, C2MemoryUsage::kSoftwareWrite }, + &block)); + ASSERT_TRUE(block); + + C2Acquirable writeViewHolder = block->map(); + C2WriteView writeView = writeViewHolder.get(); + ASSERT_EQ(C2_OK, writeView.error()); + ASSERT_EQ(kCapacity, writeView.capacity()); + ASSERT_EQ(0u, writeView.offset()); + ASSERT_EQ(kCapacity, writeView.size()); + + uint8_t *data = writeView.data(); + ASSERT_NE(nullptr, data); + for (size_t i = 0; i < writeView.size(); ++i) { + data[i] = i % 100u; + } + + C2Fence fence; + C2ConstLinearBlock constBlock = block->share( + kCapacity / 3, kCapacity / 3, fence); + + C2Acquirable readViewHolder = constBlock.map(); + C2ReadView readView = readViewHolder.get(); + ASSERT_EQ(C2_OK, readView.error()); + ASSERT_EQ(kCapacity / 3, readView.capacity()); + + // TODO: fence + const uint8_t *constData = readView.data(); + ASSERT_NE(nullptr, constData); + for (size_t i = 0; i < readView.capacity(); ++i) { + ASSERT_EQ((i + kCapacity / 3) % 100u, constData[i]) << " at i = " << i + << "; data = " << static_cast(data) + << "; constData = " << static_cast(constData); + } + + readView = readView.subView(333u, 100u); + ASSERT_EQ(C2_OK, readView.error()); + ASSERT_EQ(100u, readView.capacity()); + + constData = readView.data(); + ASSERT_NE(nullptr, constData); + for (size_t i = 0; i < readView.capacity(); ++i) { + ASSERT_EQ((i + 333u + kCapacity / 3) % 100u, constData[i]) << " at i = " << i; + } +} + +} // namespace android diff --git a/media/libstagefright/codec2/vndk/Android.bp b/media/libstagefright/codec2/vndk/Android.bp new file mode 100644 index 0000000000..9426b4edf6 --- /dev/null +++ b/media/libstagefright/codec2/vndk/Android.bp @@ -0,0 +1,30 @@ +cc_library_static { + name: "libstagefright_codec2_vndk", + + srcs: ["C2Buffer.cpp"], + + include_dirs: [ + "frameworks/av/media/libstagefright/codec2/include", + "frameworks/av/media/libstagefright/codec2/vndk/include", + "frameworks/native/include/media/hardware", + ], + + shared_libs: [ + "libbinder", + "libcutils", + "libdl", + "libhardware", + "libhidlbase", + "libion", + "liblog", + "libmedia", + "libstagefright_foundation", + "libutils", + ], + + cflags: [ + "-Werror", + "-Wall", + "-std=c++14", + ], +} diff --git a/media/libstagefright/codec2/vndk/C2Buffer.cpp b/media/libstagefright/codec2/vndk/C2Buffer.cpp new file mode 100644 index 0000000000..ffb6c2ebf0 --- /dev/null +++ b/media/libstagefright/codec2/vndk/C2Buffer.cpp @@ -0,0 +1,720 @@ +/* + * Copyright (C) 2016 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 "C2Buffer" +#include + +#include + +#include +#include + +namespace android { + +// standard ERRNO mappings +template constexpr C2Error _c2_errno2error_impl(); +template<> constexpr C2Error _c2_errno2error_impl<0>() { return C2_OK; } +template<> constexpr C2Error _c2_errno2error_impl() { return C2_BAD_VALUE; } +template<> constexpr C2Error _c2_errno2error_impl() { return C2_NO_PERMISSION; } +template<> constexpr C2Error _c2_errno2error_impl() { return C2_NO_PERMISSION; } +template<> constexpr C2Error _c2_errno2error_impl() { return C2_NO_MEMORY; } + +// map standard errno-s to the equivalent C2Error +template struct _c2_map_errno_impl; +template struct _c2_map_errno_impl { + static C2Error map(int result) { + if (result == E) { + return _c2_errno2error_impl(); + } else { + return _c2_map_errno_impl::map(result); + } + } +}; +template<> struct _c2_map_errno_impl<> { + static C2Error map(int result) { + return result == 0 ? C2_OK : C2_CORRUPTED; + } +}; + +template +C2Error c2_map_errno(int result) { + return _c2_map_errno_impl::map(result); +} + +namespace { + +// Inherit from the parent, share with the friend. + +class DummyCapacityAspect : public _C2LinearCapacityAspect { + using _C2LinearCapacityAspect::_C2LinearCapacityAspect; + friend class ::android::C2ReadView; + friend class ::android::C2ConstLinearBlock; +}; + +class C2DefaultReadView : public C2ReadView { + using C2ReadView::C2ReadView; + friend class ::android::C2ConstLinearBlock; +}; + +class C2DefaultWriteView : public C2WriteView { + using C2WriteView::C2WriteView; + friend class ::android::C2LinearBlock; +}; + +class C2AcquirableReadView : public C2Acquirable { + using C2Acquirable::C2Acquirable; + friend class ::android::C2ConstLinearBlock; +}; + +class C2AcquirableWriteView : public C2Acquirable { + using C2Acquirable::C2Acquirable; + friend class ::android::C2LinearBlock; +}; + +class C2DefaultConstLinearBlock : public C2ConstLinearBlock { + using C2ConstLinearBlock::C2ConstLinearBlock; + friend class ::android::C2LinearBlock; +}; + +class C2DefaultLinearBlock : public C2LinearBlock { + using C2LinearBlock::C2LinearBlock; + friend class ::android::C2DefaultBlockAllocator; +}; + +} // namespace + +/* ======================================= ION ALLOCATION ====================================== */ + +/** + * ION handle + */ +struct C2HandleIon : public C2Handle { + C2HandleIon(int ionFd, ion_user_handle_t buffer) : C2Handle(cHeader), + mFds{ ionFd, buffer }, + mInts{ kMagic } { } + + static bool isValid(const C2Handle * const o); + + int ionFd() const { return mFds.mIon; } + ion_user_handle_t buffer() const { return mFds.mBuffer; } + + void setBuffer(ion_user_handle_t bufferFd) { mFds.mBuffer = bufferFd; } + +protected: + struct { + int mIon; + int mBuffer; // ion_user_handle_t + } mFds; + struct { + int mMagic; + } mInts; + +private: + typedef C2HandleIon _type; + enum { + kMagic = 'ion1', + numFds = sizeof(mFds) / sizeof(int), + numInts = sizeof(mInts) / sizeof(int), + version = sizeof(C2Handle) + sizeof(mFds) + sizeof(mInts) + }; + //constexpr static C2Handle cHeader = { version, numFds, numInts, {} }; + const static C2Handle cHeader; +}; + +const C2Handle C2HandleIon::cHeader = { + C2HandleIon::version, + C2HandleIon::numFds, + C2HandleIon::numInts, + {} +}; + +// static +bool C2HandleIon::isValid(const C2Handle * const o) { + if (!o || memcmp(o, &cHeader, sizeof(cHeader))) { + return false; + } + const C2HandleIon *other = static_cast(o); + return other->mInts.mMagic == kMagic; +} + +// TODO: is the dup of an ion fd identical to ion_share? + +class C2AllocationIon : public C2LinearAllocation { +public: + virtual C2Error map( + size_t offset, size_t size, C2MemoryUsage usage, int *fence, + void **addr /* nonnull */); + virtual C2Error unmap(void *addr, size_t size, int *fenceFd); + virtual bool isValid() const; + virtual ~C2AllocationIon(); + virtual const C2Handle *handle() const; + virtual bool equals(const std::shared_ptr &other) const; + + // internal methods + C2AllocationIon(int ionFd, size_t size, size_t align, unsigned heapMask, unsigned flags); + C2AllocationIon(int ionFd, size_t size, int shareFd); + int dup() const; + C2Error status() const; + +protected: + class Impl; + Impl *mImpl; +}; + +class C2AllocationIon::Impl { +public: + // NOTE: using constructor here instead of a factory method as we will need the + // error value and this simplifies the error handling by the wrapper. + Impl(int ionFd, size_t capacity, size_t align, unsigned heapMask, unsigned flags) + : mInit(C2_OK), + mHandle(ionFd, -1), + mMapFd(-1), + mCapacity(capacity) { + ion_user_handle_t buffer = -1; + int ret = ion_alloc(mHandle.ionFd(), mCapacity, align, heapMask, flags, &buffer); + if (ret == 0) { + mHandle.setBuffer(buffer); + } else { + mInit = c2_map_errno(-ret); + } + } + + Impl(int ionFd, size_t capacity, int shareFd) + : mHandle(ionFd, -1), + mMapFd(-1), + mCapacity(capacity) { + ion_user_handle_t buffer; + mInit = ion_import(mHandle.ionFd(), shareFd, &buffer); + if (mInit == 0) { + mHandle.setBuffer(buffer); + } + (void)mCapacity; // TODO + } + + C2Error map(size_t offset, size_t size, C2MemoryUsage usage, int *fenceFd, void **addr) { + (void)fenceFd; // TODO: wait for fence + *addr = nullptr; + int prot = PROT_NONE; + int flags = MAP_PRIVATE; + if (usage.mConsumer & GRALLOC_USAGE_SW_READ_MASK) { + prot |= PROT_READ; + } + if (usage.mProducer & GRALLOC_USAGE_SW_WRITE_MASK) { + prot |= PROT_WRITE; + flags = MAP_SHARED; + } + + size_t alignmentBytes = offset % PAGE_SIZE; + size_t mapOffset = offset - alignmentBytes; + size_t mapSize = size + alignmentBytes; + + C2Error err = C2_OK; + if (mMapFd == -1) { + int ret = ion_map(mHandle.ionFd(), mHandle.buffer(), mapSize, prot, + flags, mapOffset, (unsigned char**)&mMapAddr, &mMapFd); + if (ret) { + mMapFd = -1; + *addr = nullptr; + err = c2_map_errno(-ret); + } else { + *addr = (uint8_t *)mMapAddr + alignmentBytes; + mMapAlignmentBytes = alignmentBytes; + mMapSize = mapSize; + } + } else { + mMapAddr = mmap(nullptr, mapSize, prot, flags, mMapFd, mapOffset); + if (mMapAddr == MAP_FAILED) { + mMapAddr = *addr = nullptr; + err = c2_map_errno(errno); + } else { + *addr = (uint8_t *)mMapAddr + alignmentBytes; + mMapAlignmentBytes = alignmentBytes; + mMapSize = mapSize; + } + } + return err; + } + + C2Error unmap(void *addr, size_t size, int *fenceFd) { + if (addr != (uint8_t *)mMapAddr + mMapAlignmentBytes || + size + mMapAlignmentBytes != mMapSize) { + return C2_BAD_VALUE; + } + int err = munmap(mMapAddr, mMapSize); + if (err != 0) { + return c2_map_errno(errno); + } + if (fenceFd) { + *fenceFd = -1; + } + return C2_OK; + } + + ~Impl() { + if (mMapFd != -1) { + close(mMapFd); + mMapFd = -1; + } + + (void)ion_free(mHandle.ionFd(), mHandle.buffer()); + } + + C2Error status() const { + return mInit; + } + + const C2Handle * handle() const { + return &mHandle; + } + + int dup() const { + int fd = -1; + if (mInit != 0 || ion_share(mHandle.ionFd(), mHandle.buffer(), &fd) != 0) { + fd = -1; + } + return fd; + } + +private: + C2Error mInit; + C2HandleIon mHandle; + int mMapFd; // only one for now + void *mMapAddr; + size_t mMapAlignmentBytes; + size_t mMapSize; + size_t mCapacity; +}; + +C2Error C2AllocationIon::map( + size_t offset, size_t size, C2MemoryUsage usage, int *fenceFd, void **addr) { + return mImpl->map(offset, size, usage, fenceFd, addr); +} + +C2Error C2AllocationIon::unmap(void *addr, size_t size, int *fenceFd) { + return mImpl->unmap(addr, size, fenceFd); +} + +bool C2AllocationIon::isValid() const { + return mImpl->status() == C2_OK; +} + +C2Error C2AllocationIon::status() const { + return mImpl->status(); +} + +bool C2AllocationIon::equals(const std::shared_ptr &other) const { + return other != nullptr && + other->handle(); // TODO +} + +const C2Handle *C2AllocationIon::handle() const { + return mImpl->handle(); +} + +C2AllocationIon::~C2AllocationIon() { + delete mImpl; +} + +C2AllocationIon::C2AllocationIon(int ionFd, size_t size, size_t align, unsigned heapMask, unsigned flags) + : C2LinearAllocation(size), + mImpl(new Impl(ionFd, size, align, heapMask, flags)) { } + +C2AllocationIon::C2AllocationIon(int ionFd, size_t size, int shareFd) + : C2LinearAllocation(size), + mImpl(new Impl(ionFd, size, shareFd)) { } + +int C2AllocationIon::dup() const { + return mImpl->dup(); +} + +/* ======================================= ION ALLOCATOR ====================================== */ + +C2AllocatorIon::C2AllocatorIon() : mInit(C2_OK), mIonFd(ion_open()) { + if (mIonFd < 0) { + switch (errno) { + case ENOENT: mInit = C2_UNSUPPORTED; break; + default: mInit = c2_map_errno(errno); break; + } + } +} + +C2AllocatorIon::~C2AllocatorIon() { + if (mInit == C2_OK) { + ion_close(mIonFd); + } +} + +/** + * Allocates a 1D allocation of given |capacity| and |usage|. If successful, the allocation is + * stored in |allocation|. Otherwise, |allocation| is set to 'nullptr'. + * + * \param capacity the size of requested allocation (the allocation could be slightly + * larger, e.g. to account for any system-required alignment) + * \param usage the memory usage info for the requested allocation. \note that the + * returned allocation may be later used/mapped with different usage. + * The allocator should layout the buffer to be optimized for this usage, + * but must support any usage. One exception: protected buffers can + * only be used in a protected scenario. + * \param allocation pointer to where the allocation shall be stored on success. nullptr + * will be stored here on failure + * + * \retval C2_OK the allocation was successful + * \retval C2_NO_MEMORY not enough memory to complete the allocation + * \retval C2_TIMED_OUT the allocation timed out + * \retval C2_NO_PERMISSION no permission to complete the allocation + * \retval C2_BAD_VALUE capacity or usage are not supported (invalid) (caller error) + * \retval C2_UNSUPPORTED this allocator does not support 1D allocations + * \retval C2_CORRUPTED some unknown, unrecoverable error occured during allocation (unexpected) + */ +C2Error C2AllocatorIon::allocateLinearBuffer( + uint32_t capacity, C2MemoryUsage usage, std::shared_ptr *allocation) { + *allocation = nullptr; + if (mInit != C2_OK) { + return C2_UNSUPPORTED; + } + + // get align, heapMask and flags + //size_t align = 1; + size_t align = 0; + unsigned heapMask = ~0; + unsigned flags = 0; + //TODO + (void) usage; +#if 0 + int err = mUsageMapper(usage, capacity, &align, &heapMask, &flags); + if (err < 0) { + return c2_map_errno(-err); + } +#endif + + std::shared_ptr alloc + = std::make_shared(mIonFd, capacity, align, heapMask, flags); + C2Error ret = alloc->status(); + if (ret == C2_OK) { + *allocation = alloc; + } + return ret; +} + +/** + * (Re)creates a 1D allocation from a native |handle|. If successful, the allocation is stored + * in |allocation|. Otherwise, |allocation| is set to 'nullptr'. + * + * \param handle the handle for the existing allocation + * \param allocation pointer to where the allocation shall be stored on success. nullptr + * will be stored here on failure + * + * \retval C2_OK the allocation was recreated successfully + * \retval C2_NO_MEMORY not enough memory to recreate the allocation + * \retval C2_TIMED_OUT the recreation timed out (unexpected) + * \retval C2_NO_PERMISSION no permission to recreate the allocation + * \retval C2_BAD_VALUE invalid handle (caller error) + * \retval C2_UNSUPPORTED this allocator does not support 1D allocations + * \retval C2_CORRUPTED some unknown, unrecoverable error occured during allocation (unexpected) + */ +C2Error C2AllocatorIon::recreateLinearBuffer( + const C2Handle *handle, std::shared_ptr *allocation) { + *allocation = nullptr; + if (mInit != C2_OK) { + return C2_UNSUPPORTED; + } + + if (!C2HandleIon::isValid(handle)) { + return C2_BAD_VALUE; + } + + // TODO: get capacity and validate it + const C2HandleIon *h = static_cast(handle); + std::shared_ptr alloc + = std::make_shared(mIonFd, 0 /* capacity */, h->buffer()); + C2Error ret = alloc->status(); + if (ret == C2_OK) { + *allocation = alloc; + } + return ret; +} + +/* ========================================== 1D BLOCK ========================================= */ + +class C2Block1D::Impl { +public: + const C2Handle *handle() const { + return mAllocation->handle(); + } + + Impl(std::shared_ptr alloc) + : mAllocation(alloc) {} + +private: + std::shared_ptr mAllocation; +}; + +const C2Handle *C2Block1D::handle() const { + return mImpl->handle(); +}; + +C2Block1D::C2Block1D(std::shared_ptr alloc) + : _C2LinearRangeAspect(alloc.get()), mImpl(new Impl(alloc)) { +} + +C2Block1D::C2Block1D(std::shared_ptr alloc, size_t offset, size_t size) + : _C2LinearRangeAspect(alloc.get(), offset, size), mImpl(new Impl(alloc)) { +} + +class C2ReadView::Impl { +public: + explicit Impl(const uint8_t *data) + : mData(data), mError(C2_OK) {} + + explicit Impl(C2Error error) + : mData(nullptr), mError(error) {} + + const uint8_t *data() const { + return mData; + } + + C2Error error() const { + return mError; + } + +private: + const uint8_t *mData; + C2Error mError; +}; + +C2ReadView::C2ReadView(const _C2LinearCapacityAspect *parent, const uint8_t *data) + : _C2LinearCapacityAspect(parent), mImpl(std::make_shared(data)) {} + +C2ReadView::C2ReadView(C2Error error) + : _C2LinearCapacityAspect(0u), mImpl(std::make_shared(error)) {} + +const uint8_t *C2ReadView::data() const { + return mImpl->data(); +} + +C2ReadView C2ReadView::subView(size_t offset, size_t size) const { + if (offset > capacity()) { + offset = capacity(); + } + if (size > capacity() - offset) { + size = capacity() - offset; + } + // TRICKY: newCapacity will just be used to grab the size. + DummyCapacityAspect newCapacity((uint32_t)size); + return C2ReadView(&newCapacity, data() + offset); +} + +C2Error C2ReadView::error() { + return mImpl->error(); +} + +class C2WriteView::Impl { +public: + explicit Impl(uint8_t *base) + : mBase(base), mError(C2_OK) {} + + explicit Impl(C2Error error) + : mBase(nullptr), mError(error) {} + + uint8_t *base() const { + return mBase; + } + + C2Error error() const { + return mError; + } + +private: + uint8_t *mBase; + C2Error mError; +}; + +C2WriteView::C2WriteView(const _C2LinearRangeAspect *parent, uint8_t *base) + : _C2EditableLinearRange(parent), mImpl(std::make_shared(base)) {} + +C2WriteView::C2WriteView(C2Error error) + : _C2EditableLinearRange(nullptr), mImpl(std::make_shared(error)) {} + +uint8_t *C2WriteView::base() { return mImpl->base(); } + +uint8_t *C2WriteView::data() { return mImpl->base() + offset(); } + +C2Error C2WriteView::error() { return mImpl->error(); } + +class C2ConstLinearBlock::Impl { +public: + explicit Impl(std::shared_ptr alloc) + : mAllocation(alloc), mBase(nullptr), mSize(0u), mError(C2_CORRUPTED) {} + + ~Impl() { + if (mBase != nullptr) { + // TODO: fence + C2Error err = mAllocation->unmap(mBase, mSize, nullptr); + if (err != C2_OK) { + // TODO: Log? + } + } + } + + C2ConstLinearBlock subBlock(size_t offset, size_t size) const { + return C2ConstLinearBlock(mAllocation, offset, size); + } + + void map(size_t offset, size_t size) { + if (mBase == nullptr) { + void *base = nullptr; + mError = mAllocation->map( + offset, size, { C2MemoryUsage::kSoftwareRead, 0 }, nullptr, &base); + // TODO: fence + if (mError == C2_OK) { + mBase = (uint8_t *)base; + mSize = size; + } + } + } + + const uint8_t *base() const { return mBase; } + + C2Error error() const { return mError; } + +private: + std::shared_ptr mAllocation; + uint8_t *mBase; + size_t mSize; + C2Error mError; +}; + +C2ConstLinearBlock::C2ConstLinearBlock(std::shared_ptr alloc) + : C2Block1D(alloc), mImpl(std::make_shared(alloc)) {} + +C2ConstLinearBlock::C2ConstLinearBlock( + std::shared_ptr alloc, size_t offset, size_t size) + : C2Block1D(alloc, offset, size), mImpl(std::make_shared(alloc)) {} + +C2Acquirable C2ConstLinearBlock::map() const { + mImpl->map(offset(), size()); + if (mImpl->base() == nullptr) { + C2DefaultReadView view(mImpl->error()); + return C2AcquirableReadView(mImpl->error(), mFence, view); + } + DummyCapacityAspect newCapacity(size()); + C2DefaultReadView view(&newCapacity, mImpl->base()); + return C2AcquirableReadView(mImpl->error(), mFence, view); +} + +C2ConstLinearBlock C2ConstLinearBlock::subBlock(size_t offset, size_t size) const { + return mImpl->subBlock(offset, size); +} + +class C2LinearBlock::Impl { +public: + Impl(std::shared_ptr alloc) + : mAllocation(alloc), mBase(nullptr), mSize(0u), mError(C2_CORRUPTED) {} + + ~Impl() { + if (mBase != nullptr) { + // TODO: fence + C2Error err = mAllocation->unmap(mBase, mSize, nullptr); + if (err != C2_OK) { + // TODO: Log? + } + } + } + + void map(size_t capacity) { + if (mBase == nullptr) { + void *base = nullptr; + // TODO: fence + mError = mAllocation->map( + 0u, + capacity, + { C2MemoryUsage::kSoftwareRead, C2MemoryUsage::kSoftwareWrite }, + nullptr, + &base); + if (mError == C2_OK) { + mBase = (uint8_t *)base; + mSize = capacity; + } + } + } + + C2ConstLinearBlock share(size_t offset, size_t size, C2Fence &fence) { + // TODO + (void) fence; + return C2DefaultConstLinearBlock(mAllocation, offset, size); + } + + uint8_t *base() const { return mBase; } + + C2Error error() const { return mError; } + + C2Fence fence() const { return mFence; } + +private: + std::shared_ptr mAllocation; + uint8_t *mBase; + size_t mSize; + C2Error mError; + C2Fence mFence; +}; + +C2LinearBlock::C2LinearBlock(std::shared_ptr alloc) + : C2Block1D(alloc), + mImpl(new Impl(alloc)) {} + +C2LinearBlock::C2LinearBlock(std::shared_ptr alloc, size_t offset, size_t size) + : C2Block1D(alloc, offset, size), + mImpl(new Impl(alloc)) {} + +C2Acquirable C2LinearBlock::map() { + mImpl->map(capacity()); + if (mImpl->base() == nullptr) { + C2DefaultWriteView view(mImpl->error()); + return C2AcquirableWriteView(mImpl->error(), mImpl->fence(), view); + } + C2DefaultWriteView view(this, mImpl->base()); + view.setOffset_be(offset()); + view.setSize_be(size()); + return C2AcquirableWriteView(mImpl->error(), mImpl->fence(), view); +} + +C2ConstLinearBlock C2LinearBlock::share(size_t offset, size_t size, C2Fence fence) { + return mImpl->share(offset, size, fence); +} + +C2DefaultBlockAllocator::C2DefaultBlockAllocator( + const std::shared_ptr &allocator) + : mAllocator(allocator) {} + +C2Error C2DefaultBlockAllocator::allocateLinearBlock( + uint32_t capacity, + C2MemoryUsage usage, + std::shared_ptr *block /* nonnull */) { + block->reset(); + + std::shared_ptr alloc; + C2Error err = mAllocator->allocateLinearBuffer(capacity, usage, &alloc); + if (err != C2_OK) { + return err; + } + + block->reset(new C2DefaultLinearBlock(alloc)); + + return C2_OK; +} + +} // namespace android diff --git a/media/libstagefright/codec2/vndk/include/C2BufferPriv.h b/media/libstagefright/codec2/vndk/include/C2BufferPriv.h new file mode 100644 index 0000000000..bfb069c259 --- /dev/null +++ b/media/libstagefright/codec2/vndk/include/C2BufferPriv.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2017 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 STAGEFRIGHT_CODEC2_BUFFER_PRIV_H_ +#define STAGEFRIGHT_CODEC2_BUFFER_PRIV_H_ + +#include + +#include + +namespace android { + +class C2AllocatorIon : public C2Allocator { +public: + // (usage, capacity) => (align, heapMask, flags) + typedef std::function */ size_t*, unsigned*, unsigned*)> usage_mapper_fn; + + virtual C2Error allocateLinearBuffer( + uint32_t capacity, C2MemoryUsage usage, + std::shared_ptr *allocation) override; + + virtual C2Error recreateLinearBuffer( + const C2Handle *handle, + std::shared_ptr *allocation) override; + + C2AllocatorIon(); + + C2Error status() const { return mInit; } + + virtual ~C2AllocatorIon(); + +private: + C2Error mInit; + int mIonFd; + usage_mapper_fn mUsageMapper; +}; + +class C2DefaultBlockAllocator : public C2BlockAllocator { +public: + explicit C2DefaultBlockAllocator(const std::shared_ptr &allocator); + + virtual ~C2DefaultBlockAllocator() = default; + + virtual C2Error allocateLinearBlock( + uint32_t capacity, + C2MemoryUsage usage, + std::shared_ptr *block /* nonnull */) override; + + // TODO: +private: + const std::shared_ptr mAllocator; +}; + +#if 0 +class C2Allocation::Impl { +public: + Impl() : mMapped(false), mBase(nullptr) { } + uint8_t* base() { return mMapped ? mBase : nullptr; } + + // TODO: call map... + +private: + bool mMapped; + uint8_t *mBase; +}; +#endif + +} // namespace android + +#endif // STAGEFRIGHT_CODEC2_BUFFER_PRIV_H_ -- GitLab From 6e4c5c6e30eac783c6642470b2ab24be20a8f534 Mon Sep 17 00:00:00 2001 From: Kevin Rocard Date: Thu, 27 Jul 2017 16:15:25 -0700 Subject: [PATCH 0021/1203] Align policy xml example with xsd The xml example contained an intel extension (htmi HAL) that is no longer allowed after trebbleisation. Additionally a route was specified twice which is not allowed. Bug: 38184704 Test: xmllint --noout --xinclude --schema hardware/interfaces/audio/2.0/config/audio_policy_configuration.xsd frameworks/av/services/audiopolicy/config/audio_policy_configuration.xml Change-Id: I56c3a38c840bf7757f4667cec103ad212531b071 Signed-off-by: Kevin Rocard --- .../config/audio_policy_configuration.xml | 23 +------------------ 1 file changed, 1 insertion(+), 22 deletions(-) diff --git a/services/audiopolicy/config/audio_policy_configuration.xml b/services/audiopolicy/config/audio_policy_configuration.xml index 7af2f810a7..73efe8e287 100644 --- a/services/audiopolicy/config/audio_policy_configuration.xml +++ b/services/audiopolicy/config/audio_policy_configuration.xml @@ -163,37 +163,16 @@ sources="primary output,deep_buffer,compressed_offload,BT SCO Headset Mic,Telephony Rx"/> - + sources="Built-In Mic,Built-In Back Mic,Wired Headset Mic,BT SCO Headset Mic, voice_tx"/> - - - - - - - - - - - - - - - - - -- GitLab From 9d198b59b6190c3bc4591e14cdcc0cd8dd5def87 Mon Sep 17 00:00:00 2001 From: Yahan Zhou Date: Mon, 17 Jul 2017 15:32:51 -0700 Subject: [PATCH 0022/1203] Set minimal AVC encoder level to AVCLevel1 There is a CTS test asking for a AVCLevel1 encoder. Lower the minimal level to AVCLevel1 in low resolution so that it can pass the test. BUG: 37101765 Test: run cts -m CtsMediaTestCases -t android.media.cts.MediaRecorderTest#testProfileAvcBaselineLevel1 Change-Id: Id04d3389be8d6761c6d2040a7bea54a2753a7ee7 (cherry picked from commit 085bdc87edeb5304d9dc604a4d3aa4de67cb6009) --- media/libstagefright/codecs/avcenc/SoftAVCEnc.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/media/libstagefright/codecs/avcenc/SoftAVCEnc.cpp b/media/libstagefright/codecs/avcenc/SoftAVCEnc.cpp index b1af17bffd..55ae60d4a9 100644 --- a/media/libstagefright/codecs/avcenc/SoftAVCEnc.cpp +++ b/media/libstagefright/codecs/avcenc/SoftAVCEnc.cpp @@ -629,8 +629,10 @@ OMX_ERRORTYPE SoftAVC::initEncoder() { level = 30; } else if (displaySizeY > (352 * 288)) { level = 21; - } else { + } else if (displaySizeY > (176 * 144)) { level = 20; + } else { + level = 10; } mAVCEncLevel = MAX(level, mAVCEncLevel); -- GitLab From 0a3959e84386d8e6fe40832309eeec36a599dc14 Mon Sep 17 00:00:00 2001 From: Sanna Catherine de Treville Wager Date: Tue, 25 Jul 2017 16:08:17 -0700 Subject: [PATCH 0023/1203] Simplify PerformanceAnalysis functions Instead of storing values in buffers and processing them in batches, process each sample on its own Test: dumpsys media.log Change-Id: I642555f290632cea37907a00b92f61ed6aec4eec --- media/libnbaio/PerformanceAnalysis.cpp | 226 +++++++++--------- .../include/media/nbaio/PerformanceAnalysis.h | 23 +- 2 files changed, 129 insertions(+), 120 deletions(-) diff --git a/media/libnbaio/PerformanceAnalysis.cpp b/media/libnbaio/PerformanceAnalysis.cpp index 7f4548f1d6..e9212e62d4 100644 --- a/media/libnbaio/PerformanceAnalysis.cpp +++ b/media/libnbaio/PerformanceAnalysis.cpp @@ -69,41 +69,41 @@ static int widthOf(int x) { // the timestamp series from memory. void PerformanceAnalysis::processAndFlushTimeStampSeries() { if (mTimeStampSeries.empty()) { - ALOGD("Timestamp series is empty"); return; } - // mHists is empty if thread/hash pair is sending data for the first time - if (mHists.empty()) { - mHists.emplace_front(static_cast(mTimeStampSeries[0]), - std::map()); - } - - // 1) analyze the series to store all outliers and their exact timestamps: - storeOutlierData(mTimeStampSeries); - - // 2) detect peaks in the outlier series - detectPeaks(); - - // if the current histogram has spanned its maximum time interval, - // insert a new empty histogram to the front of mHists - if (deltaMs(mHists[0].first, mTimeStampSeries[0]) >= kMaxLength.HistTimespanMs) { - mHists.emplace_front(static_cast(mTimeStampSeries[0]), - std::map()); - // When memory is full, delete oldest histogram - if (mHists.size() >= kMaxLength.Hists) { - mHists.resize(kMaxLength.Hists); + // TODO: add mPrev ts depending on whether or not handleStateChange was called + for (size_t i = 1; i < mTimeStampSeries.size(); i++) { + // Check whether the current timestamp is an outlier + const bool isOutlier = detectAndStoreOutlier(mTimeStampSeries[i]); + // If an outlier was found, check whether it was a peak + if (isOutlier) { + /*bool isPeak =*/ detectAndStorePeak( + mOutlierData[0].first, mOutlierData[0].second); } - } - - // 3) add current time intervals to histogram - for (size_t i = 1; i < mTimeStampSeries.size(); ++i) { - ++mHists[0].second[deltaMs( - mTimeStampSeries[i - 1], mTimeStampSeries[i])]; + // Insert a histogram to mHists if it is empty, or + // close the current histogram and insert a new empty one if + // if the current histogram has spanned its maximum time interval. + // Also insert a new empty histogram if a change in behavior (peak) + // occurred at the current timestamp + if (mHists.empty() || deltaMs(mHists[0].first, mTimeStampSeries[i]) >= + kMaxLength.HistTimespanMs) { + mHists.emplace_front(static_cast(mTimeStampSeries[i]), + std::map()); + // When memory is full, delete oldest histogram + // TODO: use a circular buffer + if (mHists.size() >= kMaxLength.Hists) { + mHists.resize(kMaxLength.Hists); + } + } + // add current time intervals to histogram + ++mHists[0].second[deltaMs(mTimeStampSeries[i-1], mTimeStampSeries[i])]; } // clear the timestamps mTimeStampSeries.clear(); + // reset outlier values + mOutlierDistribution.mPrevNs = -1; } // forces short-term histogram storage to avoid adding idle audio time interval @@ -130,113 +130,115 @@ void PerformanceAnalysis::logTsEntry(int64_t ts) { } } -// Given a series of outlier intervals (mOutlier data), +// Checks whether the time interval between two outliers is far enough from +// a typical delta to be considered a peak. // looks for changes in distribution (peaks), which can be either positive or negative. // The function sets the mean to the starting value and sigma to 0, and updates // them as long as no peak is detected. When a value is more than 'threshold' // standard deviations from the mean, a peak is detected and the mean and sigma // are set to the peak value and 0. -void PerformanceAnalysis::detectPeaks() { +bool PerformanceAnalysis::detectAndStorePeak(outlierInterval diff, timestamp ts) { + bool isPeak = false; if (mOutlierData.empty()) { - return; + ALOGD("mOutlierData is empty"); + return false; } - - // compute mean of the distribution. Used to check whether a value is large - const double kTypicalDiff = std::accumulate( - mOutlierData.begin(), mOutlierData.end(), 0, - [](auto &a, auto &b){return a + b.first;}) / mOutlierData.size(); - // ALOGD("typicalDiff %f", kTypicalDiff); - - // iterator at the beginning of a sequence, or updated to the most recent peak - std::deque>::iterator start = mOutlierData.begin(); - // the mean and standard deviation are updated every time a peak is detected - // initialize first time. The mean from the previous sequence is stored - // for the next sequence. Here, they are initialized for the first time. - if (mOutlierDistribution.Mean < 0) { - mOutlierDistribution.Mean = static_cast(start->first); - mOutlierDistribution.Sd = 0; + // Update mean of the distribution + // TypicalDiff is used to check whether a value is unusually large + // when we cannot use standard deviations from the mean because the sd is set to 0. + mOutlierDistribution.mTypicalDiff = (mOutlierDistribution.mTypicalDiff * + (mOutlierData.size() - 1) + diff) / mOutlierData.size(); + + // Initialize short-term mean at start of program + if (mOutlierDistribution.mMean == 0) { + mOutlierDistribution.mMean = static_cast(diff); } - auto sqr = [](auto x){ return x * x; }; - for (auto it = mOutlierData.begin(); it != mOutlierData.end(); ++it) { - // no surprise occurred: - // the new element is a small number of standard deviations from the mean - if ((fabs(it->first - mOutlierDistribution.Mean) < - mOutlierDistribution.kMaxDeviation * mOutlierDistribution.Sd) || - // or: right after peak has been detected, the delta is smaller than average - (mOutlierDistribution.Sd == 0 && - fabs(it->first - mOutlierDistribution.Mean) < kTypicalDiff)) { - // update the mean and sd: - // count number of elements (distance between start interator and current) - const int kN = std::distance(start, it) + 1; - // usual formulas for mean and sd - mOutlierDistribution.Mean = std::accumulate(start, it + 1, 0.0, - [](auto &a, auto &b){return a + b.first;}) / kN; - mOutlierDistribution.Sd = sqrt(std::accumulate(start, it + 1, 0.0, - [=](auto &a, auto &b){ - return a + sqr(b.first - mOutlierDistribution.Mean);})) / - ((kN > 1)? kN - 1 : kN); // kN - 1: mean is correlated with variance - } - // surprising value: store peak timestamp and reset mean, sd, and start iterator - else { - mPeakTimestamps.emplace_front(it->second); - // TODO: turn this into a circular buffer - if (mPeakTimestamps.size() >= kMaxLength.Peaks) { - mPeakTimestamps.resize(kMaxLength.Peaks); - } - mOutlierDistribution.Mean = static_cast(it->first); - mOutlierDistribution.Sd = 0; - start = it; + // Update length of current sequence of outliers + mOutlierDistribution.mN++; + + // If statement checks whether a large deviation from the mean occurred. + // If the standard deviation has been reset to zero, the comparison is + // instead to the mean of the full mOutlierInterval sequence. + if ((fabs(static_cast(diff) - mOutlierDistribution.mMean) < + mOutlierDistribution.kMaxDeviation * mOutlierDistribution.mSd) || + (mOutlierDistribution.mSd == 0 && + fabs(diff - mOutlierDistribution.mMean) < + mOutlierDistribution.mTypicalDiff)) { + // update the mean and sd using online algorithm + // https://en.wikipedia.org/wiki/ + // Algorithms_for_calculating_variance#Online_algorithm + mOutlierDistribution.mN++; + const double kDelta = diff - mOutlierDistribution.mMean; + mOutlierDistribution.mMean += kDelta / mOutlierDistribution.mN; + const double kDelta2 = diff - mOutlierDistribution.mMean; + mOutlierDistribution.mM2 += kDelta * kDelta2; + mOutlierDistribution.mSd = (mOutlierDistribution.mN < 2) ? 0 : + mOutlierDistribution.mM2 / (mOutlierDistribution.mN - 1); + } else { + // new value is far from the mean: + // store peak timestamp and reset mean, sd, and short-term sequence + isPeak = true; + mPeakTimestamps.emplace_front(ts); + // if mPeaks has reached capacity, delete oldest data + // Note: this means that mOutlierDistribution values do not exactly + // match the data we have in mPeakTimestamps, but this is not an issue + // in practice for estimating future peaks. + // TODO: turn this into a circular buffer + if (mPeakTimestamps.size() >= kMaxLength.Peaks) { + mPeakTimestamps.resize(kMaxLength.Peaks); } + mOutlierDistribution.mMean = 0; + mOutlierDistribution.mSd = 0; + mOutlierDistribution.mN = 0; + mOutlierDistribution.mM2 = 0; } - return; + ALOGD("outlier distr %f %f", mOutlierDistribution.mMean, mOutlierDistribution.mSd); + return isPeak; } -// Called by LogTsEntry. The input is a vector of timestamps. -// Finds outliers and writes to mOutlierdata. -// Each value in mOutlierdata consists of: . +// Determines whether the difference between a timestamp and the previous +// one is beyond a threshold. If yes, stores the timestamp as an outlier +// and writes to mOutlierdata in the following format: +// Time elapsed since previous outlier: Timestamp of start of outlier // e.g. timestamps (ms) 1, 4, 5, 16, 18, 28 will produce pairs (4, 5), (13, 18). -// This function is applied to the time series before it is converted into a histogram. -void PerformanceAnalysis::storeOutlierData(const std::vector ×tamps) { - if (timestamps.size() < 1) { - return; - } +bool PerformanceAnalysis::detectAndStoreOutlier(const int64_t ts) { // first pass: need to initialize - if (mOutlierDistribution.Elapsed == 0) { - mOutlierDistribution.PrevNs = timestamps[0]; + if (mOutlierDistribution.mPrevNs == -1) { + mOutlierDistribution.mPrevNs = ts; } - for (const auto &ts: timestamps) { - const uint64_t diffMs = static_cast(deltaMs(mOutlierDistribution.PrevNs, ts)); - if (diffMs >= static_cast(kOutlierMs)) { - mOutlierData.emplace_front(mOutlierDistribution.Elapsed, - static_cast(mOutlierDistribution.PrevNs)); - // Remove oldest value if the vector is full - // TODO: remove pop_front once circular buffer is in place - // FIXME: make sure kShortHistSize is large enough that that data will never be lost - // before being written to file or to a FIFO - if (mOutlierData.size() >= kMaxLength.Outliers) { - mOutlierData.resize(kMaxLength.Outliers); - } - mOutlierDistribution.Elapsed = 0; + bool isOutlier = false; + const uint64_t diffMs = static_cast(deltaMs(mOutlierDistribution.mPrevNs, ts)); + // ALOGD("%d\t", static_cast(diffMs)); + if (diffMs >= static_cast(kOutlierMs)) { + isOutlier = true; + //ALOGD("outlier outlier %d %d", static_cast(diffMs), + // static_cast(mOutlierDistribution.mElapsed)); + mOutlierData.emplace_front(mOutlierDistribution.mElapsed, + static_cast(mOutlierDistribution.mPrevNs)); + // Remove oldest value if the vector is full + // TODO: turn this into a circular buffer + // TODO: make sure kShortHistSize is large enough that that data will never be lost + // before being written to file or to a FIFO + if (mOutlierData.size() >= kMaxLength.Outliers) { + mOutlierData.resize(kMaxLength.Outliers); } - mOutlierDistribution.Elapsed += diffMs; - mOutlierDistribution.PrevNs = ts; + mOutlierDistribution.mElapsed = 0; } + mOutlierDistribution.mElapsed += diffMs; + mOutlierDistribution.mPrevNs = ts; + return isOutlier; } // TODO Make it return a std::string instead of modifying body --> is this still relevant? // TODO consider changing all ints to uint32_t or uint64_t // TODO: move this to ReportPerformance, probably make it a friend function of PerformanceAnalysis void PerformanceAnalysis::reportPerformance(String8 *body, int maxHeight) { - // Add any new data - processAndFlushTimeStampSeries(); - if (mHists.empty()) { ALOGD("reportPerformance: mHists is empty"); return; } ALOGD("reportPerformance: hists size %d", static_cast(mHists.size())); - // TODO: more elaborate data analysis + std::map buckets; for (const auto &shortHist: mHists) { for (const auto &countPair : shortHist.second) { @@ -308,7 +310,6 @@ void PerformanceAnalysis::reportPerformance(String8 *body, int maxHeight) { body->appendFormat("%lld: %lld\n", static_cast(outlier.first), static_cast(outlier.second)); } - } // TODO: decide whether to use this or whether it is overkill, and it is enough @@ -340,22 +341,25 @@ void PerformanceAnalysis::alertIfGlitch(const std::vector &samples) { // writes summary of performance into specified file descriptor void dump(int fd, int indent, PerformanceAnalysisMap &threadPerformanceAnalysis) { String8 body; + int i = 0; // TODO: delete const char* const kDirectory = "/data/misc/audioserver/"; for (auto & thread : threadPerformanceAnalysis) { for (auto & hash: thread.second) { + ALOGD("hash number %d", i++); PerformanceAnalysis& curr = hash.second; + // Add any new data curr.processAndFlushTimeStampSeries(); // write performance data to console curr.reportPerformance(&body); + if (!body.isEmpty()) { + dumpLine(fd, indent, body); + body.clear(); + } // write to file writeToFile(curr.mHists, curr.mOutlierData, curr.mPeakTimestamps, kDirectory, false, thread.first, hash.first); } } - if (!body.isEmpty()) { - dumpLine(fd, indent, body); - body.clear(); - } } // Writes a string into specified file descriptor diff --git a/media/libnbaio/include/media/nbaio/PerformanceAnalysis.h b/media/libnbaio/include/media/nbaio/PerformanceAnalysis.h index 81f9c582be..77fa23a535 100644 --- a/media/libnbaio/include/media/nbaio/PerformanceAnalysis.h +++ b/media/libnbaio/include/media/nbaio/PerformanceAnalysis.h @@ -67,12 +67,12 @@ public: // Input: mOutlierData. Looks at time elapsed between outliers // finds significant changes in the distribution // writes timestamps of significant changes to mPeakTimestamps - void detectPeaks(); + bool detectAndStorePeak(outlierInterval delta, timestamp ts); // runs analysis on timestamp series before it is converted to a histogram // finds outliers // writes to mOutlierData