Buffer Log functionality for AudioFlinger
Similar to ALOGV, this allows to capture to file audio samples from
within audioservice for debuggin purposes.
Test: manual. Mostly used for debugging
Change-Id: I4d43f573926805a27be910e343476c3f1be51579
diff --git a/services/audioflinger/BufLog.cpp b/services/audioflinger/BufLog.cpp
new file mode 100644
index 0000000..9680eb5
--- /dev/null
+++ b/services/audioflinger/BufLog.cpp
@@ -0,0 +1,195 @@
+/*
+ * 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.
+ */
+
+
+#include "BufLog.h"
+#define LOG_TAG "BufLog"
+//#define LOG_NDEBUG 0
+
+#include <errno.h>
+#include "log/log.h"
+#include <pthread.h>
+#include <stdio.h>
+#include <string.h>
+
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+
+// ------------------------------
+// BufLogSingleton
+// ------------------------------
+pthread_once_t onceControl = PTHREAD_ONCE_INIT;
+
+BufLog *BufLogSingleton::mInstance = NULL;
+
+void BufLogSingleton::initOnce() {
+ mInstance = new BufLog();
+ ALOGW("=====================================\n" \
+ "Warning: BUFLOG is defined in some part of your code.\n" \
+ "This will create large audio dumps in %s.\n" \
+ "=====================================\n", BUFLOG_BASE_PATH);
+}
+
+BufLog *BufLogSingleton::instance() {
+ pthread_once(&onceControl, initOnce);
+ return mInstance;
+}
+
+bool BufLogSingleton::instanceExists() {
+ return mInstance != NULL;
+}
+
+// ------------------------------
+// BufLog
+// ------------------------------
+
+BufLog::BufLog() {
+ memset(mStreams, 0, sizeof(mStreams));
+}
+
+BufLog::~BufLog() {
+ android::Mutex::Autolock autoLock(mLock);
+
+ for (unsigned int id = 0; id < BUFLOG_MAXSTREAMS; id++) {
+ BufLogStream *pBLStream = mStreams[id];
+ if (pBLStream != NULL) {
+ delete pBLStream ;
+ mStreams[id] = NULL;
+ }
+ }
+}
+
+size_t BufLog::write(int streamid, const char *tag, int format, int channels,
+ int samplingRate, size_t maxBytes, const void *buf, size_t size) {
+ unsigned int id = streamid % BUFLOG_MAXSTREAMS;
+ android::Mutex::Autolock autoLock(mLock);
+
+ BufLogStream *pBLStream = mStreams[id];
+
+ if (pBLStream == NULL) {
+ pBLStream = mStreams[id] = new BufLogStream(id, tag, format, channels,
+ samplingRate, maxBytes);
+ ALOG_ASSERT(pBLStream != NULL, "BufLogStream Failed to be created");
+ }
+
+ return pBLStream->write(buf, size);
+}
+
+void BufLog::reset() {
+ android::Mutex::Autolock autoLock(mLock);
+ ALOGV("Resetting all BufLogs");
+ int count = 0;
+
+ for (unsigned int id = 0; id < BUFLOG_MAXSTREAMS; id++) {
+ BufLogStream *pBLStream = mStreams[id];
+ if (pBLStream != NULL) {
+ delete pBLStream;
+ mStreams[id] = NULL;
+ count++;
+ }
+ }
+ ALOGV("Reset %d BufLogs", count);
+}
+
+// ------------------------------
+// BufLogStream
+// ------------------------------
+
+BufLogStream::BufLogStream(unsigned int id,
+ const char *tag,
+ unsigned int format,
+ unsigned int channels,
+ unsigned int samplingRate,
+ size_t maxBytes = 0) : mId(id), mFormat(format), mChannels(channels),
+ mSamplingRate(samplingRate), mMaxBytes(maxBytes) {
+ mByteCount = 0l;
+ mPaused = false;
+ if (tag != NULL) {
+ strncpy(mTag, tag, BUFLOGSTREAM_MAX_TAGSIZE);
+ } else {
+ mTag[0] = 0;
+ }
+ ALOGV("Creating BufLogStream id:%d tag:%s format:%d ch:%d sr:%d maxbytes:%zu", mId, mTag,
+ mFormat, mChannels, mSamplingRate, mMaxBytes);
+
+ //open file (s), info about tag, format, etc.
+ //timestamp
+ char timeStr[16]; //size 16: format %Y%m%d%H%M%S 14 chars + string null terminator
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ struct tm tm;
+ localtime_r(&tv.tv_sec, &tm);
+ strftime(timeStr, sizeof(timeStr), "%Y%m%d%H%M%S", &tm);
+ char logPath[BUFLOG_MAX_PATH_SIZE];
+ snprintf(logPath, BUFLOG_MAX_PATH_SIZE, "%s/%s_%d_%s_%d_%d_%d.raw", BUFLOG_BASE_PATH, timeStr,
+ mId, mTag, mFormat, mChannels, mSamplingRate);
+ ALOGV("data output: %s", logPath);
+
+ mFile = fopen(logPath, "wb");
+ if (mFile != NULL) {
+ ALOGV("Success creating file at: %p", mFile);
+ } else {
+ ALOGE("Error: could not create file BufLogStream %s", strerror(errno));
+ }
+}
+
+void BufLogStream::closeStream_l() {
+ ALOGV("Closing BufLogStream id:%d tag:%s", mId, mTag);
+ if (mFile != NULL) {
+ fclose(mFile);
+ mFile = NULL;
+ }
+}
+
+BufLogStream::~BufLogStream() {
+ ALOGV("Destroying BufLogStream id:%d tag:%s", mId, mTag);
+ android::Mutex::Autolock autoLock(mLock);
+ closeStream_l();
+}
+
+size_t BufLogStream::write(const void *buf, size_t size) {
+
+ size_t bytes = 0;
+ if (!mPaused && mFile != NULL) {
+ if (size > 0 && buf != NULL) {
+ android::Mutex::Autolock autoLock(mLock);
+ if (mMaxBytes > 0) {
+ size = MIN(size, mMaxBytes - mByteCount);
+ }
+ bytes = fwrite(buf, 1, size, mFile);
+ mByteCount += bytes;
+ if (mMaxBytes > 0 && mMaxBytes == mByteCount) {
+ closeStream_l();
+ }
+ }
+ ALOGV("wrote %zu/%zu bytes to BufLogStream %d tag:%s. Total Bytes: %zu", bytes, size, mId,
+ mTag, mByteCount);
+ } else {
+ ALOGV("Warning: trying to write to %s BufLogStream id:%d tag:%s",
+ mPaused ? "paused" : "closed", mId, mTag);
+ }
+ return bytes;
+}
+
+bool BufLogStream::setPause(bool pause) {
+ bool old = mPaused;
+ mPaused = pause;
+ return old;
+}
+
+void BufLogStream::finalize() {
+ android::Mutex::Autolock autoLock(mLock);
+ closeStream_l();
+}