一、概述
对于音频数据问题,如音效、重音问题,在不确定问题出现在那个阶段时,可以把各个阶段的音频数据dump出来,然后用audacity工具播放这些dump出来的pcm音频文件,定义问题出现的阶段,然后针对性的分析和解决问题。
1. 音频数据流概述
Application Data
↓
AudioTrack::write() → 复制到共享缓冲区
↓
AudioTrack::releaseBuffer() → 更新控制块
↓
AudioFlinger::threadLoop() → 主循环
↓
AudioMixer::process() → 音频混合
↓
Track::interceptBuffer() → 处理原始数据 (dumpAtcppData)
↓
Track::releaseBuffer() → 处理混合后数据 (dumpAtcppEndData)
↓
threadLoop_write() → 写入HAL
↓
HAL → PCM输出
AudioTrack::write()
├── obtainBuffer(&audioBuffer, blocking ? &ClientProxy::kForever : &ClientProxy::kNonBlocking)
├── memcpy(audioBuffer.i8, buffer, toWrite) // 复制数据到共享缓冲区
└── releaseBuffer(&audioBuffer) // 释放缓冲区,通知AudioFlinger
AudioTrack::releaseBuffer(const Buffer* audioBuffer)
├── mProxy->releaseBuffer(&buffer) // 更新共享控制块
└── restartIfDisabled() // 如果track被禁用则重启
//AudioTrack::start()会启动播放线程,该现象会循环查询是否有活跃track
AudioFlinger::PlaybackThread::threadLoop()
├── 检查活跃tracks //有活跃track就进行混音,然后写入hal
├── 如果mBytesRemaining == 0:
│ ├── 如果mMixerStatus == MIXER_TRACKS_READY:
│ │ └── threadLoop_mix() // 混合音频数据,AudioFlinger::MixerThread::threadLoop_mix()
│ └── 否则:
│ └── threadLoop_sleepTime() // 等待或填充静音
└── 如果mSleepTimeUs == 0:
└── threadLoop_write() //写入HAL,AudioFlinger::MixerThread::threadLoop_write()
AudioFlinger::PlaybackThread::write()
AudioMixer::process() //AudioFlinger::MixerThread::threadLoop_mix()
├── 对每个活跃track调用:
│ ├── track->getNextBuffer(&buffer) // 获取音频数据,AudioFlinger::PlaybackThread::Track::getNextBuffer
│ ├── 音频混合处理
│ └── track->releaseBuffer(&buffer) // 释放缓冲区
└── 混合后的数据写入mMixerBuffer
AudioFlinger::ThreadBase::TrackBase::releaseBuffer(AudioBufferProvider::Buffer* buffer)
├── interceptBuffer(*buffer) // 处理原始数据(应用传下来的)
│ └── dumpAtcppData(buffer->raw, bytes) // ATCPP dump
└── TrackBase::releaseBuffer(buffer) // 处理经过AudioFlinger的数据
└── dumpAtcppEndData(buffer->raw, bytes) // ATCPP End dump
AudioFlinger::PlaybackThread::threadLoop_write()
├── 如果mNormalSink != 0:
│ └── mNormalSink->write((char *)mSinkBuffer + offset, count)
└── 否则:
└── mOutput->write((char *)mSinkBuffer + offset, mBytesRemaining)
└── HAL层写入PCM
out->stream.write = out_write;@audio_hw.c (hal)
out_write(struct audio_stream_out *stream, const void* buffer,size_t bytes)
start_output_stream(out);
dump_out_data(buffer, bytes); //在往pcm写之前,dump音频源数据到文件里
pcm_write(out->pcm[i], (void *)out_buffer, outFrameCount*2*2);
二、dump音频数据
1.dump混音前应用传下来的原始音频数据的位置:AudioFlinger::PlaybackThread::Track::getNextBuffer
2.dump混音后是音频数据的位置:AudioFlinger::PlaybackThread::Track::releaseBuffer
3.dump AF写入hal的音频数据的位置:AudioFlinger::PlaybackThread::threadLoop_write()
4.dump hal写入音频设备前的音频数据的位置:out->stream.write = out_write;@audio_hw.c
调试属性值
static void dump_out_data(const void* buffer,size_t bytes)
setprop vendor.audio.record 3 //setenforce 0
static void dump_in_data(const void* buffer, size_t bytes)
setprop vendor.audio.record.in 3
setprop vendor.audio.record.af 4 //dump AudioFlinger::MixerThread::threadLoop_write()中播放线程最终写入hal的音频数据
/data/misc/audioserver/af_out.pcm
setprop vendor.audio.record.afmix 4 //dump AudioFlinger::MixerThread::threadLoop_mix()中播放线程混音前的音频数据
/data/misc/audioserver/af_out_fmix.pcm
setprop vendor.audio.record.atcpp 4 //dump AudioFlinger::PlaybackThread::Track::releaseBuffer中track混音后的数据(Track::releaseBuffer-》interceptBuffer(*buffer);-> TrackBase::releaseBuffer(buffer),在interceptBuffer中dump)
/data/misc/audioserver/atcpp_out.pcm
setprop vendor.audio.record.atcppe 4 //dump AudioFlinger::PlaybackThread::Track::releaseBuffer中track混音后的数据(Track::releaseBuffer-》interceptBuffer(*buffer);-> TrackBase::releaseBuffer(buffer),在TrackBase::releaseBuffer中dump)
/data/misc/audioserver/atcpp_out_e.pcm
setprop vendor.audio.record.atcppo 4 //dump AudioFlinger::PlaybackThread::Track::getNextBuffer中track混音前的数据
/data/misc/audioserver/atcpp_out_o.pcm
diff --git a/frameworks/av/services/audioflinger/Threads.cpp b/frameworks/av/services/audioflinger/Threads.cpp
old mode 100644
new mode 100755
index 7a4eded9f60..fd749cd8338
--- a/frameworks/av/services/audioflinger/Threads.cpp
+++ b/frameworks/av/services/audioflinger/Threads.cpp
@@ -29,8 +29,10 @@
#include
#include
#include
+#include
#include
#include
+#include
#include
#include
#include
@@ -533,7 +535,32 @@ AudioFlinger::ThreadBase::ThreadBase(const sp
// mName will be set by concrete (non-virtual) subclass
mDeathRecipient(new PMDeathRecipient(this)),
mSystemReady(systemReady),
- mSignalPending(false)
+ mSignalPending(false),
+ // Initialize audio dump members
+ mAudioDumpEnabled(true), // 默认启用音频dump
+ mAudioDumpFd(-1),
+ mAudioDumpPath("/data/misc/audioserver/af_out.pcm"),
+ mAudioDumpFrameCount(0),
+ // Initialize pre-mix audio dump members
+ mPreMixDumpEnabled(true), // 默认启用混音前dump
+ mPreMixDumpFd(-1),
+ mPreMixDumpPath("/data/misc/audioserver/af_out_fmix.pcm"),
+ mPreMixDumpFrameCount(0),
+ // Initialize ATCPP audio dump members
+ mAtcppDumpEnabled(true), // 默认启用ATCPP dump
+ mAtcppDumpFd(-1),
+ mAtcppDumpPath("/data/misc/audioserver/atcpp_out.pcm"),
+ mAtcppDumpFrameCount(0),
+ // Initialize ATCPP Original audio dump members
+ mAtcppOriginalDumpEnabled(true), // 默认启用ATCPP Original dump
+ mAtcppOriginalDumpFd(-1),
+ mAtcppOriginalDumpPath("/data/misc/audioserver/atcpp_out_o.pcm"),
+ mAtcppOriginalDumpFrameCount(0),
+ // Initialize ATCPP End audio dump members
+ mAtcppEndDumpEnabled(true), // 默认启用ATCPP End dump
+ mAtcppEndDumpFd(-1),
+ mAtcppEndDumpPath("/data/misc/audioserver/atcpp_out_e.pcm"),
+ mAtcppEndDumpFrameCount(0)
{
mThreadMetrics.logConstructor(getpid(), threadTypeToString(type), id);
memset(&mPatch, 0, sizeof(struct audio_patch));
@@ -544,6 +571,36 @@ AudioFlinger::ThreadBase::~ThreadBase()
// mConfigEvents should be empty, but just in case it isn't, free the memory it owns
mConfigEvents.clear();
+ // Close audio dump file if open
+ if (mAudioDumpFd >= 0) {
+ close(mAudioDumpFd);
+ mAudioDumpFd = -1;
+ }
+
+ // Close pre-mix audio dump file if open
+ if (mPreMixDumpFd >= 0) {
+ close(mPreMixDumpFd);
+ mPreMixDumpFd = -1;
+ }
+
+ // Close ATCPP audio dump file if open
+ if (mAtcppDumpFd >= 0) {
+ close(mAtcppDumpFd);
+ mAtcppDumpFd = -1;
+ }
+
+ // Close ATCPP Original audio dump file if open
+ if (mAtcppOriginalDumpFd >= 0) {
+ close(mAtcppOriginalDumpFd);
+ mAtcppOriginalDumpFd = -1;
+ }
+
+ // Close ATCPP End audio dump file if open
+ if (mAtcppEndDumpFd >= 0) {
+ close(mAtcppEndDumpFd);
+ mAtcppEndDumpFd = -1;
+ }
+
// do not lock the mutex in destructor
releaseWakeLock_l();
if (mPowerManager != 0) {
@@ -3338,6 +3395,10 @@ ssize_t AudioFlinger::PlaybackThread::threadLoop_write()
mThreadMetrics.logBeginInterval();
mStandby = false;
}
+
+ // Dump audio data
+ dumpAudioData((char *)mSinkBuffer + offset, bytesWritten);
+
return bytesWritten;
}
@@ -5023,6 +5084,18 @@ void AudioFlinger::PlaybackThread::onAsyncError()
void AudioFlinger::MixerThread::threadLoop_mix()
{
+ // Dump pre-mix audio data before mixing
+ ALOGV("threadLoop_mix: mMixerBufferValid=%d, mMixerBuffer=%p",
+ mMixerBufferValid, mMixerBuffer);
+ if (mMixerBufferValid && mMixerBuffer != nullptr) {
+ // Calculate mixer buffer size using PlaybackThread member variables
+ size_t mixerBufferSize = mNormalFrameCount * mChannelCount * audio_bytes_per_sample(mMixerBufferFormat);
+ ALOGV("threadLoop_mix: calling dumpPreMixData with size=%zu", mixerBufferSize);
+ dumpPreMixData(mMixerBuffer, mixerBufferSize);
+ } else {
+ ALOGV("threadLoop_mix: skipping dumpPreMixData - buffer not valid");
+ }
+
// mix buffers...
mAudioMixer->process();
mCurrentWriteLength = mSinkBufferSize;
@@ -10480,4 +10553,761 @@ status_t AudioFlinger::MmapCaptureThread::getExternalPosition(
return mInput->getCapturePosition((int64_t*)position, timeNanos);
}
+// Audio dump functionality implementation
+void AudioFlinger::ThreadBase::enableAudioDump(bool enable)
+{
+ Mutex::Autolock _l(mLock);
+ mAudioDumpEnabled = enable;
+
+ if (!enable && mAudioDumpFd >= 0) {
+ close(mAudioDumpFd);
+ mAudioDumpFd = -1;
+ mAudioDumpFrameCount = 0;
+ ALOGD("Audio dump disabled, closed file");
+ } else if (enable) {
+ // Create directory if it doesn't exist
+ const char* pathStr = mAudioDumpPath.string();
+ const char* lastSlash = strrchr(pathStr, '/');
+ if (lastSlash != nullptr) {
+ char dirPath[PATH_MAX];
+ size_t dirLen = lastSlash - pathStr;
+ if (dirLen < sizeof(dirPath)) {
+ strncpy(dirPath, pathStr, dirLen);
+ dirPath[dirLen] = '\0';
+ mkdir(dirPath, 0755);
+ }
+ }
+ ALOGD("Audio dump enabled, will save to: %s", mAudioDumpPath.string());
+ }
+}
+
+void AudioFlinger::ThreadBase::enablePreMixDump(bool enable)
+{
+ Mutex::Autolock _l(mLock);
+ mPreMixDumpEnabled = enable;
+
+ if (!enable && mPreMixDumpFd >= 0) {
+ close(mPreMixDumpFd);
+ mPreMixDumpFd = -1;
+ mPreMixDumpFrameCount = 0;
+ ALOGD("Pre-mix audio dump disabled, closed file");
+ } else if (enable) {
+ // Create directory if it doesn't exist
+ const char* pathStr = mPreMixDumpPath.string();
+ const char* lastSlash = strrchr(pathStr, '/');
+ if (lastSlash != nullptr) {
+ char dirPath[PATH_MAX];
+ size_t dirLen = lastSlash - pathStr;
+ if (dirLen < sizeof(dirPath)) {
+ strncpy(dirPath, pathStr, dirLen);
+ dirPath[dirLen] = '\0';
+ mkdir(dirPath, 0755);
+ }
+ }
+ ALOGD("Pre-mix audio dump enabled, will save to: %s", mPreMixDumpPath.string());
+ }
+}
+
+void AudioFlinger::ThreadBase::setAudioDumpPath(const String8& path)
+{
+ Mutex::Autolock _l(mLock);
+ mAudioDumpPath = path;
+
+ // Create directory if it doesn't exist
+ const char* pathStr = mAudioDumpPath.string();
+ const char* lastSlash = strrchr(pathStr, '/');
+ if (lastSlash != nullptr) {
+ char dirPath[PATH_MAX];
+ size_t dirLen = lastSlash - pathStr;
+ if (dirLen < sizeof(dirPath)) {
+ strncpy(dirPath, pathStr, dirLen);
+ dirPath[dirLen] = '\0';
+ mkdir(dirPath, 0755);
+ }
+ }
+
+ ALOGD("Audio dump path set to: %s", path.string());
+}
+
+void AudioFlinger::ThreadBase::dumpAudioData(const void* buffer, size_t bytes)
+{
+ if (!mAudioDumpEnabled || buffer == nullptr || bytes == 0) {
+ return;
+ }
+
+ // Check system property for dump control
+ char value[PROPERTY_VALUE_MAX];
+ property_get("vendor.audio.record.af", value, "0");
+ int size = atoi(value);
+ if (size <= 0) {
+ return; // 属性为0或负数时禁用dump
+ }
+
+ // Check if we need to open the dump file
+ if (mAudioDumpFd < 0) {
+ // Create directory if it doesn't exist
+ const char* pathStr = mAudioDumpPath.string();
+ const char* lastSlash = strrchr(pathStr, '/');
+ if (lastSlash != nullptr) {
+ char dirPath[PATH_MAX];
+ size_t dirLen = lastSlash - pathStr;
+ if (dirLen < sizeof(dirPath)) {
+ strncpy(dirPath, pathStr, dirLen);
+ dirPath[dirLen] = '\0';
+ mkdir(dirPath, 0755);
+ }
+ }
+
+ // Open dump file
+ mAudioDumpFd = open(mAudioDumpPath.string(), O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ if (mAudioDumpFd < 0) {
+ ALOGE("Failed to open audio dump file: %s, error: %s",
+ mAudioDumpPath.string(), strerror(errno));
+ return;
+ }
+
+ // Log format information for debugging
+ ALOGD("Opened audio dump file: %s", mAudioDumpPath.string());
+ ALOGD("Post-mix dump format info:");
+ ALOGD(" - Sample Rate: %u Hz", mSampleRate);
+ ALOGD(" - Channel Count: %u", mChannelCount);
+ ALOGD(" - Channel Mask: 0x%x", mChannelMask);
+ ALOGD(" - Format: 0x%x", mFormat);
+ ALOGD(" - Frame Size: %zu bytes", mFrameSize);
+ ALOGD(" - Bytes per sample: %zu", audio_bytes_per_sample(mFormat));
+
+ // Calculate and log the actual format for playback
+ size_t bytesPerSample = audio_bytes_per_sample(mFormat);
+ size_t actualFrameSize = mChannelCount * bytesPerSample;
+ ALOGD(" - Actual frame size: %zu bytes", actualFrameSize);
+ ALOGD(" - Estimated playback format:");
+ ALOGD(" * Sample Rate: %u Hz", mSampleRate);
+ ALOGD(" * Channels: %u", mChannelCount);
+ ALOGD(" * Bits per sample: %zu", bytesPerSample * 8);
+ ALOGD(" * Format: %s", (mFormat == AUDIO_FORMAT_PCM_16_BIT) ? "PCM 16-bit" :
+ (mFormat == AUDIO_FORMAT_PCM_FLOAT) ? "PCM Float" :
+ (mFormat == AUDIO_FORMAT_PCM_8_BIT) ? "PCM 8-bit" :
+ (mFormat == AUDIO_FORMAT_PCM_24_BIT_PACKED) ? "PCM 24-bit" :
+ (mFormat == AUDIO_FORMAT_PCM_32_BIT) ? "PCM 32-bit" : "Unknown");
+ }
+
+ // Write audio data to file
+ ssize_t written = write(mAudioDumpFd, buffer, bytes);
+ if (written < 0) {
+ ALOGE("Failed to write audio data to dump file: %s", strerror(errno));
+ return;
+ } else if ((size_t)written != bytes) {
+ ALOGW("Partial write to audio dump file: %zd of %zu bytes", written, bytes);
+ }
+
+ // Update frame count (assuming 16-bit samples, 2 channels)
+ // This is a rough estimation, actual frame count depends on audio format
+ size_t frameSize = mChannelCount * 2; // 2 bytes per sample for 16-bit
+ if (frameSize > 0) {
+ mAudioDumpFrameCount += bytes / frameSize;
+ }
+
+ // Check if we've reached the maximum dump duration based on property
+ if (mAudioDumpFrameCount >= size * 1024 * 1024 / frameSize) {
+ ALOGD("Reached maximum dump duration (%d MB), stopping audio dump", size);
+ enableAudioDump(false);
+ property_set("vendor.audio.record.af", "0"); // 自动重置属性
+ }
+
+ ALOGV("Dumped %zu bytes of audio data, total frames: %lld",
+ bytes, (long long)mAudioDumpFrameCount);
+}
+
+void AudioFlinger::ThreadBase::setPreMixDumpPath(const String8& path)
+{
+ Mutex::Autolock _l(mLock);
+ mPreMixDumpPath = path;
+
+ // Create directory if it doesn't exist
+ const char* pathStr = mPreMixDumpPath.string();
+ const char* lastSlash = strrchr(pathStr, '/');
+ if (lastSlash != nullptr) {
+ char dirPath[PATH_MAX];
+ size_t dirLen = lastSlash - pathStr;
+ if (dirLen < sizeof(dirPath)) {
+ strncpy(dirPath, pathStr, dirLen);
+ dirPath[dirLen] = '\0';
+ mkdir(dirPath, 0755);
+ }
+ }
+
+ ALOGD("Pre-mix audio dump path set to: %s", path.string());
+}
+
+void AudioFlinger::ThreadBase::dumpPreMixData(const void* buffer, size_t bytes)
+{
+ ALOGV("dumpPreMixData called: enabled=%d, buffer=%p, bytes=%zu",
+ mPreMixDumpEnabled, buffer, bytes);
+
+ if (!mPreMixDumpEnabled || buffer == nullptr || bytes == 0) {
+ ALOGV("dumpPreMixData early return: enabled=%d, buffer=%p, bytes=%zu",
+ mPreMixDumpEnabled, buffer, bytes);
+ return;
+ }
+
+ // Check system property for dump control
+ char value[PROPERTY_VALUE_MAX];
+ property_get("vendor.audio.record.afmix", value, "0");
+ int size = atoi(value);
+ ALOGV("dumpPreMixData property check: vendor.audio.record.afmix=%s, size=%d", value, size);
+ if (size <= 0) {
+ ALOGV("dumpPreMixData disabled by property: size=%d", size);
+ return; // 属性为0或负数时禁用dump
+ }
+
+ // Check if we need to open the dump file
+ if (mPreMixDumpFd < 0) {
+ // Create directory if it doesn't exist
+ const char* pathStr = mPreMixDumpPath.string();
+ const char* lastSlash = strrchr(pathStr, '/');
+ if (lastSlash != nullptr) {
+ char dirPath[PATH_MAX];
+ size_t dirLen = lastSlash - pathStr;
+ if (dirLen < sizeof(dirPath)) {
+ strncpy(dirPath, pathStr, dirLen);
+ dirPath[dirLen] = '\0';
+ mkdir(dirPath, 0755);
+ }
+ }
+
+ // Open dump file
+ mPreMixDumpFd = open(mPreMixDumpPath.string(), O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ if (mPreMixDumpFd < 0) {
+ ALOGE("Failed to open pre-mix audio dump file: %s, error: %s",
+ mPreMixDumpPath.string(), strerror(errno));
+ return;
+ }
+
+ // Log format information for debugging
+ ALOGD("Opened pre-mix audio dump file: %s", mPreMixDumpPath.string());
+ ALOGD("Pre-mix dump format info:");
+ ALOGD(" - Sample Rate: %u Hz", mSampleRate);
+ ALOGD(" - Channel Count: %u", mChannelCount);
+ ALOGD(" - Channel Mask: 0x%x", mChannelMask);
+ ALOGD(" - Frame Size: %zu bytes", mFrameSize);
+ ALOGD(" - Bytes per sample: %zu", audio_bytes_per_sample(mFormat));
+
+ // Calculate and log the actual format for playback
+ size_t bytesPerSample = audio_bytes_per_sample(mFormat);
+ size_t actualFrameSize = mChannelCount * bytesPerSample;
+ ALOGD(" - Actual frame size: %zu bytes", actualFrameSize);
+ ALOGD(" - Estimated playback format:");
+ ALOGD(" * Sample Rate: %u Hz", mSampleRate);
+ ALOGD(" * Channels: %u", mChannelCount);
+ ALOGD(" * Bits per sample: %zu", bytesPerSample * 8);
+ ALOGD(" * Format: %s", (mFormat == AUDIO_FORMAT_PCM_16_BIT) ? "PCM 16-bit" :
+ (mFormat == AUDIO_FORMAT_PCM_FLOAT) ? "PCM Float" :
+ (mFormat == AUDIO_FORMAT_PCM_8_BIT) ? "PCM 8-bit" :
+ (mFormat == AUDIO_FORMAT_PCM_24_BIT_PACKED) ? "PCM 24-bit" :
+ (mFormat == AUDIO_FORMAT_PCM_32_BIT) ? "PCM 32-bit" : "Unknown");
+ }
+
+ // Write audio data to file
+ ssize_t written = write(mPreMixDumpFd, buffer, bytes);
+ if (written < 0) {
+ ALOGE("Failed to write pre-mix audio data to dump file: %s", strerror(errno));
+ return;
+ } else if ((size_t)written != bytes) {
+ ALOGW("Partial write to pre-mix audio dump file: %zd of %zu bytes", written, bytes);
+ }
+
+ // Update frame count based on actual format
+ size_t bytesPerSample = audio_bytes_per_sample(mFormat);
+ size_t actualFrameSize = mChannelCount * bytesPerSample;
+ if (actualFrameSize > 0) {
+ mPreMixDumpFrameCount += bytes / actualFrameSize;
+ }
+
+ // Check if we've reached the maximum dump duration based on property
+ if (mPreMixDumpFrameCount >= size * 1024 * 1024 / actualFrameSize) {
+ ALOGD("Reached maximum pre-mix dump duration (%d MB), stopping pre-mix audio dump", size);
+ enablePreMixDump(false);
+ property_set("vendor.audio.record.afmix", "0"); // 自动重置属性
+ }
+
+ ALOGV("Dumped %zu bytes of pre-mix audio data, total frames: %lld",
+ bytes, (long long)mPreMixDumpFrameCount);
+}
+
+void AudioFlinger::ThreadBase::enableAtcppDump(bool enable)
+{
+ Mutex::Autolock _l(mLock);
+ mAtcppDumpEnabled = enable;
+
+ if (!enable && mAtcppDumpFd >= 0) {
+ close(mAtcppDumpFd);
+ mAtcppDumpFd = -1;
+ mAtcppDumpFrameCount = 0;
+ ALOGD("ATCPP audio dump disabled, closed file");
+ } else if (enable) {
+ // Create directory if it doesn't exist
+ const char* pathStr = mAtcppDumpPath.string();
+ const char* lastSlash = strrchr(pathStr, '/');
+ if (lastSlash != nullptr) {
+ char dirPath[PATH_MAX];
+ size_t dirLen = lastSlash - pathStr;
+ if (dirLen < sizeof(dirPath)) {
+ strncpy(dirPath, pathStr, dirLen);
+ dirPath[dirLen] = '\0';
+ mkdir(dirPath, 0755);
+ }
+ }
+ ALOGD("ATCPP audio dump enabled, will save to: %s", mAtcppDumpPath.string());
+ }
+}
+
+void AudioFlinger::ThreadBase::setAtcppDumpPath(const String8& path)
+{
+ Mutex::Autolock _l(mLock);
+ mAtcppDumpPath = path;
+
+ // Create directory if it doesn't exist
+ const char* pathStr = mAtcppDumpPath.string();
+ const char* lastSlash = strrchr(pathStr, '/');
+ if (lastSlash != nullptr) {
+ char dirPath[PATH_MAX];
+ size_t dirLen = lastSlash - pathStr;
+ if (dirLen < sizeof(dirPath)) {
+ strncpy(dirPath, pathStr, dirLen);
+ dirPath[dirLen] = '\0';
+ mkdir(dirPath, 0755);
+ }
+ }
+
+ ALOGD("ATCPP audio dump path set to: %s", path.string());
+}
+
+void AudioFlinger::ThreadBase::dumpAtcppData(const void* buffer, size_t bytes)
+{
+ ALOGV("dumpAtcppData called: enabled=%d, buffer=%p, bytes=%zu",
+ mAtcppDumpEnabled, buffer, bytes);
+
+ if (!mAtcppDumpEnabled || buffer == nullptr || bytes == 0) {
+ ALOGV("dumpAtcppData early return: enabled=%d, buffer=%p, bytes=%zu",
+ mAtcppDumpEnabled, buffer, bytes);
+ return;
+ }
+
+ // Check system property for dump control
+ char value[PROPERTY_VALUE_MAX];
+ property_get("vendor.audio.record.atcpp", value, "0");
+ int size = atoi(value);
+ ALOGV("dumpAtcppData property check: vendor.audio.record.atcpp=%s, size=%d", value, size);
+ if (size <= 0) {
+ ALOGV("dumpAtcppData disabled by property: size=%d", size);
+ return; // 属性为0或负数时禁用dump
+ }
+
+ // Check if we need to open the dump file
+ if (mAtcppDumpFd < 0) {
+ // Create directory if it doesn't exist
+ const char* pathStr = mAtcppDumpPath.string();
+ const char* lastSlash = strrchr(pathStr, '/');
+ if (lastSlash != nullptr) {
+ char dirPath[PATH_MAX];
+ size_t dirLen = lastSlash - pathStr;
+ if (dirLen < sizeof(dirPath)) {
+ strncpy(dirPath, pathStr, dirLen);
+ dirPath[dirLen] = '\0';
+ mkdir(dirPath, 0755);
+ }
+ }
+
+ // Open dump file
+ mAtcppDumpFd = open(mAtcppDumpPath.string(), O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ if (mAtcppDumpFd < 0) {
+ ALOGE("Failed to open ATCPP audio dump file: %s, error: %s",
+ mAtcppDumpPath.string(), strerror(errno));
+ return;
+ }
+
+ // Log format information for debugging
+ ALOGD("Opened ATCPP audio dump file: %s", mAtcppDumpPath.string());
+ ALOGD("ATCPP dump format info:");
+ ALOGD(" - Sample Rate: %u Hz", mSampleRate);
+ ALOGD(" - Channel Count: %u", mChannelCount);
+ ALOGD(" - Channel Mask: 0x%x", mChannelMask);
+ ALOGD(" - Frame Size: %zu bytes", mFrameSize);
+ ALOGD(" - Bytes per sample: %zu", audio_bytes_per_sample(mFormat));
+
+ // Calculate and log the actual format for playback
+ size_t bytesPerSample = audio_bytes_per_sample(mFormat);
+ size_t actualFrameSize = mChannelCount * bytesPerSample;
+ ALOGD(" - Actual frame size: %zu bytes", actualFrameSize);
+ ALOGD(" - Estimated playback format:");
+ ALOGD(" * Sample Rate: %u Hz", mSampleRate);
+ ALOGD(" * Channels: %u", mChannelCount);
+ ALOGD(" * Bits per sample: %zu", bytesPerSample * 8);
+ ALOGD(" * Format: %s", (mFormat == AUDIO_FORMAT_PCM_16_BIT) ? "PCM 16-bit" :
+ (mFormat == AUDIO_FORMAT_PCM_FLOAT) ? "PCM Float" :
+ (mFormat == AUDIO_FORMAT_PCM_8_BIT) ? "PCM 8-bit" :
+ (mFormat == AUDIO_FORMAT_PCM_24_BIT_PACKED) ? "PCM 24-bit" :
+ (mFormat == AUDIO_FORMAT_PCM_32_BIT) ? "PCM 32-bit" : "Unknown");
+ }
+
+ // Write audio data to file
+ ssize_t written = write(mAtcppDumpFd, buffer, bytes);
+ if (written < 0) {
+ ALOGE("Failed to write ATCPP audio data to dump file: %s", strerror(errno));
+ return;
+ } else if ((size_t)written != bytes) {
+ ALOGW("Partial write to ATCPP audio dump file: %zd of %zu bytes", written, bytes);
+ }
+
+ // Update frame count based on actual format
+ size_t bytesPerSample = audio_bytes_per_sample(mFormat);
+ size_t actualFrameSize = mChannelCount * bytesPerSample;
+ if (actualFrameSize > 0) {
+ mAtcppDumpFrameCount += bytes / actualFrameSize;
+ }
+
+ // Check if we've reached the maximum dump duration based on property
+ if (mAtcppDumpFrameCount >= size * 1024 * 1024 / actualFrameSize) {
+ ALOGD("Reached maximum ATCPP dump duration (%d MB), stopping ATCPP audio dump", size);
+ enableAtcppDump(false);
+ property_set("vendor.audio.record.atcpp", "0"); // 自动重置属性
+ }
+
+ ALOGV("Dumped %zu bytes of ATCPP audio data, total frames: %lld",
+ bytes, (long long)mAtcppDumpFrameCount);
+}
+
+void AudioFlinger::ThreadBase::enableAtcppOriginalDump(bool enable)
+{
+ Mutex::Autolock _l(mLock);
+ mAtcppOriginalDumpEnabled = enable;
+
+ if (!enable && mAtcppOriginalDumpFd >= 0) {
+ close(mAtcppOriginalDumpFd);
+ mAtcppOriginalDumpFd = -1;
+ mAtcppOriginalDumpFrameCount = 0;
+ ALOGD("ATCPP Original audio dump disabled, closed file");
+ } else if (enable) {
+ // Create directory if it doesn't exist
+ const char* pathStr = mAtcppOriginalDumpPath.string();
+ const char* lastSlash = strrchr(pathStr, '/');
+ if (lastSlash != nullptr) {
+ char dirPath[PATH_MAX];
+ size_t dirLen = lastSlash - pathStr;
+ if (dirLen < sizeof(dirPath)) {
+ strncpy(dirPath, pathStr, dirLen);
+ dirPath[dirLen] = '\0';
+ mkdir(dirPath, 0755);
+ }
+ }
+
+ // Open file for writing
+ mAtcppOriginalDumpFd = open(pathStr, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ if (mAtcppOriginalDumpFd >= 0) {
+ mAtcppOriginalDumpFrameCount = 0;
+ ALOGD("ATCPP Original audio dump enabled, opened file: %s", pathStr);
+ } else {
+ ALOGE("Failed to open ATCPP Original dump file: %s, error: %s",
+ pathStr, strerror(errno));
+ }
+ }
+}
+
+void AudioFlinger::ThreadBase::setAtcppOriginalDumpPath(const String8& path)
+{
+ Mutex::Autolock _l(mLock);
+ mAtcppOriginalDumpPath = path;
+ ALOGD("ATCPP Original dump path set to: %s", path.string());
+}
+
+void AudioFlinger::ThreadBase::dumpAtcppOriginalData(const void* buffer, size_t bytes)
+{
+ ALOGV("dumpAtcppOriginalData called: enabled=%d, buffer=%p, bytes=%zu",
+ mAtcppOriginalDumpEnabled, buffer, bytes);
+
+ if (!mAtcppOriginalDumpEnabled || buffer == nullptr || bytes == 0) {
+ ALOGV("dumpAtcppOriginalData early return: enabled=%d, buffer=%p, bytes=%zu",
+ mAtcppOriginalDumpEnabled, buffer, bytes);
+ return;
+ }
+
+ // Check system property for dump control
+ char value[PROPERTY_VALUE_MAX];
+ property_get("vendor.audio.record.atcppo", value, "0");
+ int size = atoi(value);
+ ALOGV("dumpAtcppOriginalData property check: vendor.audio.record.atcppo=%s, size=%d", value, size);
+ if (size <= 0) {
+ ALOGV("dumpAtcppOriginalData disabled by property: size=%d", size);
+ return; // 属性为0或负数时禁用dump
+ }
+
+ // Check if we need to open the dump file
+ if (mAtcppOriginalDumpFd < 0) {
+ // Create directory if it doesn't exist
+ const char* pathStr = mAtcppOriginalDumpPath.string();
+ const char* lastSlash = strrchr(pathStr, '/');
+ if (lastSlash != nullptr) {
+ char dirPath[PATH_MAX];
+ size_t dirLen = lastSlash - pathStr;
+ if (dirLen < sizeof(dirPath)) {
+ strncpy(dirPath, pathStr, dirLen);
+ dirPath[dirLen] = '\0';
+ mkdir(dirPath, 0755);
+ }
+ }
+
+ // Open dump file
+ mAtcppOriginalDumpFd = open(mAtcppOriginalDumpPath.string(), O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ if (mAtcppOriginalDumpFd < 0) {
+ ALOGE("Failed to open ATCPP Original audio dump file: %s, error: %s",
+ mAtcppOriginalDumpPath.string(), strerror(errno));
+ return;
+ }
+
+ // Log format information for debugging
+ ALOGD("Opened ATCPP Original audio dump file: %s", mAtcppOriginalDumpPath.string());
+ ALOGD("ATCPP Original dump format info:");
+ ALOGD(" - Sample Rate: %u Hz", mSampleRate);
+ ALOGD(" - Channel Count: %u", mChannelCount);
+ ALOGD(" - Channel Mask: 0x%x", mChannelMask);
+ ALOGD(" - Frame Size: %zu bytes", mFrameSize);
+ ALOGD(" - Bytes per sample: %zu", audio_bytes_per_sample(mFormat));
+
+ // Calculate and log the actual format for playback
+ size_t bytesPerSample = audio_bytes_per_sample(mFormat);
+ size_t actualFrameSize = mChannelCount * bytesPerSample;
+ ALOGD(" - Actual frame size: %zu bytes", actualFrameSize);
+ ALOGD(" - Estimated playback format:");
+ ALOGD(" * Sample Rate: %u Hz", mSampleRate);
+ ALOGD(" * Channels: %u", mChannelCount);
+ ALOGD(" * Bits per sample: %zu", bytesPerSample * 8);
+ ALOGD(" * Format: %s", (mFormat == AUDIO_FORMAT_PCM_16_BIT) ? "PCM 16-bit" :
+ (mFormat == AUDIO_FORMAT_PCM_FLOAT) ? "PCM Float" :
+ (mFormat == AUDIO_FORMAT_PCM_8_BIT) ? "PCM 8-bit" :
+ (mFormat == AUDIO_FORMAT_PCM_24_BIT_PACKED) ? "PCM 24-bit" :
+ (mFormat == AUDIO_FORMAT_PCM_32_BIT) ? "PCM 32-bit" : "Unknown");
+ }
+
+ // Write audio data to file
+ ssize_t written = write(mAtcppOriginalDumpFd, buffer, bytes);
+ if (written < 0) {
+ ALOGE("Failed to write ATCPP Original audio data to dump file: %s", strerror(errno));
+ return;
+ } else if ((size_t)written != bytes) {
+ ALOGW("Partial write to ATCPP Original audio dump file: %zd of %zu bytes", written, bytes);
+ }
+
+ // Debug: Check if the data contains actual audio (not silence)
+ if (bytes > 0) {
+ const int16_t* samples = static_cast
+ size_t numSamples = bytes / sizeof(int16_t);
+ bool hasAudio = false;
+ for (size_t i = 0; i < numSamples && i < 100; i++) { // Check first 100 samples
+ if (samples[i] != 0) {
+ hasAudio = true;
+ break;
+ }
+ }
+ ALOGV("ATCPP Original dump: %zu bytes, %zu samples, hasAudio=%s",
+ bytes, numSamples, hasAudio ? "true" : "false");
+ }
+
+ // Update frame count based on actual format
+ size_t bytesPerSample = audio_bytes_per_sample(mFormat);
+ size_t actualFrameSize = mChannelCount * bytesPerSample;
+ if (actualFrameSize > 0) {
+ mAtcppOriginalDumpFrameCount += bytes / actualFrameSize;
+ }
+
+ // Check if we've reached the maximum dump duration based on property
+ size_t maxFrames = size * 1024 * 1024 / actualFrameSize;
+ ALOGV("ATCPP Original dump progress: %lld/%zu frames (%.1f%%)",
+ (long long)mAtcppOriginalDumpFrameCount, maxFrames,
+ (float)mAtcppOriginalDumpFrameCount * 100.0f / maxFrames);
+
+ if (mAtcppOriginalDumpFrameCount >= maxFrames) {
+ ALOGD("Reached maximum ATCPP Original dump duration (%d MB), stopping ATCPP Original audio dump", size);
+ property_set("vendor.audio.record.atcppo", "0"); // 自动重置属性
+ // 关闭文件但不禁用dump功能,这样下次设置属性时还能重新启动
+ if (mAtcppOriginalDumpFd >= 0) {
+ close(mAtcppOriginalDumpFd);
+ mAtcppOriginalDumpFd = -1;
+ mAtcppOriginalDumpFrameCount = 0;
+ }
+ }
+
+ ALOGV("Dumped %zu bytes of ATCPP Original audio data, total frames: %lld",
+ bytes, (long long)mAtcppOriginalDumpFrameCount);
+}
+
+void AudioFlinger::ThreadBase::enableAtcppEndDump(bool enable)
+{
+ Mutex::Autolock _l(mLock);
+ mAtcppEndDumpEnabled = enable;
+
+ if (!enable && mAtcppEndDumpFd >= 0) {
+ close(mAtcppEndDumpFd);
+ mAtcppEndDumpFd = -1;
+ mAtcppEndDumpFrameCount = 0;
+ ALOGD("ATCPP End audio dump disabled, closed file");
+ } else if (enable) {
+ // Create directory if it doesn't exist
+ const char* pathStr = mAtcppEndDumpPath.string();
+ const char* lastSlash = strrchr(pathStr, '/');
+ if (lastSlash != nullptr) {
+ char dirPath[PATH_MAX];
+ size_t dirLen = lastSlash - pathStr;
+ if (dirLen < sizeof(dirPath)) {
+ strncpy(dirPath, pathStr, dirLen);
+ dirPath[dirLen] = '\0';
+ mkdir(dirPath, 0755);
+ }
+ }
+ ALOGD("ATCPP End audio dump enabled, will save to: %s", mAtcppEndDumpPath.string());
+ }
+}
+
+void AudioFlinger::ThreadBase::setAtcppEndDumpPath(const String8& path)
+{
+ Mutex::Autolock _l(mLock);
+ mAtcppEndDumpPath = path;
+
+ // Create directory if it doesn't exist
+ const char* pathStr = mAtcppEndDumpPath.string();
+ const char* lastSlash = strrchr(pathStr, '/');
+ if (lastSlash != nullptr) {
+ char dirPath[PATH_MAX];
+ size_t dirLen = lastSlash - pathStr;
+ if (dirLen < sizeof(dirPath)) {
+ strncpy(dirPath, pathStr, dirLen);
+ dirPath[dirLen] = '\0';
+ mkdir(dirPath, 0755);
+ }
+ }
+
+ ALOGD("ATCPP End audio dump path set to: %s", path.string());
+}
+
+void AudioFlinger::ThreadBase::dumpAtcppEndData(const void* buffer, size_t bytes)
+{
+ ALOGV("dumpAtcppEndData called: enabled=%d, buffer=%p, bytes=%zu",
+ mAtcppEndDumpEnabled, buffer, bytes);
+
+ if (!mAtcppEndDumpEnabled || buffer == nullptr || bytes == 0) {
+ ALOGV("dumpAtcppEndData early return: enabled=%d, buffer=%p, bytes=%zu",
+ mAtcppEndDumpEnabled, buffer, bytes);
+ return;
+ }
+
+ // Check system property for dump control
+ char value[PROPERTY_VALUE_MAX];
+ property_get("vendor.audio.record.atcppe", value, "0");
+ int size = atoi(value);
+ ALOGV("dumpAtcppEndData property check: vendor.audio.record.atcppe=%s, size=%d", value, size);
+ if (size <= 0) {
+ ALOGV("dumpAtcppEndData disabled by property: size=%d", size);
+ return; // 属性为0或负数时禁用dump
+ }
+
+ // Check if we need to open the dump file
+ if (mAtcppEndDumpFd < 0) {
+ // Create directory if it doesn't exist
+ const char* pathStr = mAtcppEndDumpPath.string();
+ const char* lastSlash = strrchr(pathStr, '/');
+ if (lastSlash != nullptr) {
+ char dirPath[PATH_MAX];
+ size_t dirLen = lastSlash - pathStr;
+ if (dirLen < sizeof(dirPath)) {
+ strncpy(dirPath, pathStr, dirLen);
+ dirPath[dirLen] = '\0';
+ mkdir(dirPath, 0755);
+ }
+ }
+
+ // Open dump file
+ mAtcppEndDumpFd = open(mAtcppEndDumpPath.string(), O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ if (mAtcppEndDumpFd < 0) {
+ ALOGE("Failed to open ATCPP End audio dump file: %s, error: %s",
+ mAtcppEndDumpPath.string(), strerror(errno));
+ return;
+ }
+
+ // Log format information for debugging
+ ALOGD("Opened ATCPP End audio dump file: %s", mAtcppEndDumpPath.string());
+ ALOGD("ATCPP End dump format info:");
+ ALOGD(" - Sample Rate: %u Hz", mSampleRate);
+ ALOGD(" - Channel Count: %u", mChannelCount);
+ ALOGD(" - Channel Mask: 0x%x", mChannelMask);
+ ALOGD(" - Frame Size: %zu bytes", mFrameSize);
+ ALOGD(" - Bytes per sample: %zu", audio_bytes_per_sample(mFormat));
+
+ // Calculate and log the actual format for playback
+ size_t bytesPerSample = audio_bytes_per_sample(mFormat);
+ size_t actualFrameSize = mChannelCount * bytesPerSample;
+ ALOGD(" - Actual frame size: %zu bytes", actualFrameSize);
+ ALOGD(" - Estimated playback format:");
+ ALOGD(" * Sample Rate: %u Hz", mSampleRate);
+ ALOGD(" * Channels: %u", mChannelCount);
+ ALOGD(" * Bits per sample: %zu", bytesPerSample * 8);
+ ALOGD(" * Format: %s", (mFormat == AUDIO_FORMAT_PCM_16_BIT) ? "PCM 16-bit" :
+ (mFormat == AUDIO_FORMAT_PCM_FLOAT) ? "PCM Float" :
+ (mFormat == AUDIO_FORMAT_PCM_8_BIT) ? "PCM 8-bit" :
+ (mFormat == AUDIO_FORMAT_PCM_24_BIT_PACKED) ? "PCM 24-bit" :
+ (mFormat == AUDIO_FORMAT_PCM_32_BIT) ? "PCM 32-bit" : "Unknown");
+ }
+
+ // Write audio data to file
+ ssize_t written = write(mAtcppEndDumpFd, buffer, bytes);
+ if (written < 0) {
+ ALOGE("Failed to write ATCPP End audio data to dump file: %s", strerror(errno));
+ return;
+ } else if ((size_t)written != bytes) {
+ ALOGW("Partial write to ATCPP End audio dump file: %zd of %zu bytes", written, bytes);
+ }
+
+ // Debug: Check if the data contains actual audio (not silence)
+ if (bytes > 0) {
+ const int16_t* samples = static_cast
+ size_t numSamples = bytes / sizeof(int16_t);
+ bool hasAudio = false;
+ for (size_t i = 0; i < numSamples && i < 100; i++) { // Check first 100 samples
+ if (samples[i] != 0) {
+ hasAudio = true;
+ break;
+ }
+ }
+ ALOGV("ATCPP End dump: %zu bytes, %zu samples, hasAudio=%s",
+ bytes, numSamples, hasAudio ? "true" : "false");
+ }
+
+ // Update frame count based on actual format
+ size_t bytesPerSample = audio_bytes_per_sample(mFormat);
+ size_t actualFrameSize = mChannelCount * bytesPerSample;
+ if (actualFrameSize > 0) {
+ mAtcppEndDumpFrameCount += bytes / actualFrameSize;
+ }
+
+ // Check if we've reached the maximum dump duration based on property
+ size_t maxFrames = size * 1024 * 1024 / actualFrameSize;
+ ALOGV("ATCPP End dump progress: %lld/%zu frames (%.1f%%)",
+ (long long)mAtcppEndDumpFrameCount, maxFrames,
+ (float)mAtcppEndDumpFrameCount * 100.0f / maxFrames);
+
+ if (mAtcppEndDumpFrameCount >= maxFrames) {
+ ALOGD("Reached maximum ATCPP End dump duration (%d MB), stopping ATCPP End audio dump", size);
+ property_set("vendor.audio.record.atcppe", "0"); // 自动重置属性
+ // 关闭文件但不禁用dump功能,这样下次设置属性时还能重新启动
+ if (mAtcppEndDumpFd >= 0) {
+ close(mAtcppEndDumpFd);
+ mAtcppEndDumpFd = -1;
+ mAtcppEndDumpFrameCount = 0;
+ }
+ }
+
+ ALOGV("Dumped %zu bytes of ATCPP End audio data, total frames: %lld",
+ bytes, (long long)mAtcppEndDumpFrameCount);
+}
+
+
+
} // namespace android
diff --git a/frameworks/av/services/audioflinger/Threads.h b/frameworks/av/services/audioflinger/Threads.h
old mode 100644
new mode 100755
index 8561de367ef..884f317c165
--- a/frameworks/av/services/audioflinger/Threads.h
+++ b/frameworks/av/services/audioflinger/Threads.h
@@ -612,6 +612,31 @@ protected:
{ }
virtual void dumpTracks_l(int fd __unused, const Vector
+ // Audio dump functionality
+ virtual void enableAudioDump(bool enable);
+ virtual void setAudioDumpPath(const String8& path);
+ virtual void dumpAudioData(const void* buffer, size_t bytes);
+
+ // Pre-mix audio dump functionality
+ virtual void enablePreMixDump(bool enable);
+ virtual void setPreMixDumpPath(const String8& path);
+ virtual void dumpPreMixData(const void* buffer, size_t bytes);
+
+ // ATCPP (AudioTrack Client to Playback) dump functionality
+ virtual void enableAtcppDump(bool enable);
+ virtual void setAtcppDumpPath(const String8& path);
+ virtual void dumpAtcppData(const void* buffer, size_t bytes);
+
+ // ATCPP Original (original audio data from client) dump functionality
+ virtual void enableAtcppOriginalDump(bool enable);
+ virtual void setAtcppOriginalDumpPath(const String8& path);
+ virtual void dumpAtcppOriginalData(const void* buffer, size_t bytes);
+
+ // ATCPP End (AudioFlinger processed data) dump functionality
+ virtual void enableAtcppEndDump(bool enable);
+ virtual void setAtcppEndDumpPath(const String8& path);
+ virtual void dumpAtcppEndData(const void* buffer, size_t bytes);
+
friend class AudioFlinger; // for mEffectChains
@@ -701,6 +726,37 @@ protected:
// we must not wait for async write callback in the thread loop before evaluating it
bool mSignalPending;
+ // Audio dump functionality
+ bool mAudioDumpEnabled;
+ int mAudioDumpFd;
+ String8 mAudioDumpPath;
+ int64_t mAudioDumpFrameCount;
+ static const int64_t kMaxDumpFrames = 48000 * 10; // 10 seconds at 48kHz
+
+ // Pre-mix audio dump functionality
+ bool mPreMixDumpEnabled;
+ int mPreMixDumpFd;
+ String8 mPreMixDumpPath;
+ int64_t mPreMixDumpFrameCount;
+
+ // ATCPP (AudioTrack Client to Playback) dump functionality
+ bool mAtcppDumpEnabled;
+ int mAtcppDumpFd;
+ String8 mAtcppDumpPath;
+ int64_t mAtcppDumpFrameCount;
+
+ // ATCPP Original (original audio data from client) dump functionality
+ bool mAtcppOriginalDumpEnabled;
+ int mAtcppOriginalDumpFd;
+ String8 mAtcppOriginalDumpPath;
+ int64_t mAtcppOriginalDumpFrameCount;
+
+ // ATCPP End (AudioFlinger processed data) dump functionality
+ bool mAtcppEndDumpEnabled;
+ int mAtcppEndDumpFd;
+ String8 mAtcppEndDumpPath;
+ int64_t mAtcppEndDumpFrameCount;
+
#ifdef TEE_SINK
NBAIO_Tee mTee;
#endif
diff --git a/frameworks/av/services/audioflinger/Tracks.cpp b/frameworks/av/services/audioflinger/Tracks.cpp
old mode 100644
new mode 100755
index bbe5e70bf5b..904450935e4
--- a/frameworks/av/services/audioflinger/Tracks.cpp
+++ b/frameworks/av/services/audioflinger/Tracks.cpp
@@ -290,6 +290,21 @@ void AudioFlinger::ThreadBase::TrackBase::releaseBuffer(AudioBufferProvider::Buf
mTee.write(buffer->raw, buffer->frameCount);
#endif
+ // ATCPP End dump - dump AudioFlinger processed data
+ if (buffer->raw != nullptr && buffer->frameCount > 0) {
+ sp
+ if (thread != nullptr) {
+ size_t bytes = buffer->frameCount * mFrameSize;
+ thread->dumpAtcppEndData(buffer->raw, bytes);
+
+ // Log audio format information for debugging
+ ALOGV("ATCPP End dump: frameCount=%zu, frameSize=%zu, bytes=%zu, "
+ "sampleRate=%u, channelCount=%u, format=%d",
+ buffer->frameCount, mFrameSize, bytes,
+ mSampleRate, mChannelCount, mFormat);
+ }
+ }
+
ServerProxy::Buffer buf;
buf.mFrameCount = buffer->frameCount;
buf.mRaw = buffer->raw;
@@ -945,6 +960,22 @@ status_t AudioFlinger::PlaybackThread::Track::getNextBuffer(AudioBufferProvider:
status_t status = mServerProxy->obtainBuffer(&buf);
buffer->frameCount = buf.mFrameCount;
buffer->raw = buf.mRaw;
+
+ // ATCPP Original dump - dump the original audio data from client (before AudioFlinger processing)
+ if (buf.mRaw != nullptr && buf.mFrameCount > 0) {
+ sp
+ if (thread != nullptr) {
+ size_t bytes = buf.mFrameCount * mFrameSize;
+ thread->dumpAtcppOriginalData(buf.mRaw, bytes);
+
+ // Log audio format information for debugging
+ ALOGV("ATCPP Original dump: frameCount=%zu, frameSize=%zu, bytes=%zu, "
+ "sampleRate=%u, channelCount=%u, format=%d",
+ buf.mFrameCount, mFrameSize, bytes,
+ mSampleRate, mChannelCount, mFormat);
+ }
+ }
+
if (buf.mFrameCount == 0 && !isStopping() && !isStopped() && !isPaused() && !isOffloaded()) {
ALOGV("%s(%d): underrun, framesReady(%zu) < framesDesired(%zd), state: %d",
__func__, mId, buf.mFrameCount, desiredFrames, (int)mState);
@@ -971,6 +1002,14 @@ void AudioFlinger::PlaybackThread::Track::interceptBuffer(
// Additionally PatchProxyBufferProvider::obtainBuffer (called by PathTrack::getNextBuffer)
// does not allow 0 frame size request contrary to getNextBuffer
}
+
+ // ATCPP dump: dump the original audio data from client
+ sp
+ if (thread != 0) {
+ size_t bytes = frameCount * mFrameSize;
+ thread->dumpAtcppData(sourceBuffer.i8, bytes);
+ }
+
for (auto& teePatch : mTeePatches) {
RecordThread::PatchRecord* patchRecord = teePatch.patchRecord.get();
const size_t framesWritten = patchRecord->writeFrames(
diff --git a/hardware/rockchip/audio/tinyalsa_hal/audio_hw.c b/hardware/rockchip/audio/tinyalsa_hal/audio_hw.c
index 9ac59de92d3..8d30a65384b 100755
--- a/hardware/rockchip/audio/tinyalsa_hal/audio_hw.c
+++ b/hardware/rockchip/audio/tinyalsa_hal/audio_hw.c
@@ -160,6 +160,8 @@ FILE *fp_out;
#endif
#endif
+
+
int in_dump(const struct audio_stream *stream, int fd);
int out_dump(const struct audio_stream *stream, int fd);
#ifdef A3386_RS_AUDIO
@@ -2190,38 +2192,91 @@ static void dump_out_data(const void* buffer,size_t bytes)
}
}
-static void dump_in_data(const void* buffer, size_t bytes)
+// 为每个stream_in维护独立的dump状态
+struct dump_state {
+ FILE* fd;
+ int offset;
+ void* stream_ptr;
+};
+
+// 使用简单的数组来管理多个stream的dump状态
+#define MAX_DUMP_STREAMS 10
+static struct dump_state dump_states[MAX_DUMP_STREAMS] = {0};
+static pthread_mutex_t dump_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static void dump_in_data(const void* buffer, size_t bytes, struct stream_in* in)
{
- static int offset = 0;
- static FILE* fd = NULL;
char value[PROPERTY_VALUE_MAX];
property_get("vendor.audio.record.in", value, "0");
int size = atoi(value);
- if (size > 0) {
- if(fd == NULL) {
- fd=fopen("/data/misc/audioserver/debug_in.pcm","wb+");
- if(fd == NULL) {
- ALOGD("DEBUG open /data/misc/audioserver/debug_in.pcm ,errno = %s",strerror(errno));
- } else {
- ALOGD("dump pcm to file /data/misc/audioserver/debug_in.pcm");
- }
- offset = 0;
+
+ if (size <= 0 || in == NULL) {
+ return;
+ }
+
+ pthread_mutex_lock(&dump_mutex);
+
+ // 查找这个stream的dump状态
+ int index = -1;
+ for (int i = 0; i < MAX_DUMP_STREAMS; i++) {
+ if (dump_states[i].stream_ptr == (void*)in) {
+ index = i;
+ break;
}
}
-
- if (fd != NULL) {
- ALOGD("dump in pcm %zu bytes", bytes);
- fwrite(buffer,bytes,1,fd);
- offset += bytes;
- fflush(fd);
- if (offset >= size*1024*1024) {
- fclose(fd);
- fd = NULL;
- offset = 0;
- property_set("vendor.audio.record.in", "0");
- ALOGD("TEST record pcmfile end");
+
+ // 如果没找到,分配一个新的位置
+ if (index == -1) {
+ for (int i = 0; i < MAX_DUMP_STREAMS; i++) {
+ if (dump_states[i].stream_ptr == NULL) {
+ index = i;
+ break;
+ }
+ }
+ // 如果所有位置都被占用,使用第一个位置
+ if (index == -1) {
+ index = 0;
+ if (dump_states[index].fd != NULL) {
+ fclose(dump_states[index].fd);
+ dump_states[index].fd = NULL;
+ }
+ }
+ dump_states[index].stream_ptr = (void*)in;
+ dump_states[index].offset = 0;
+ }
+
+ // 如果是新的stream或文件未打开
+ if (dump_states[index].fd == NULL) {
+ char filename[256];
+ snprintf(filename, sizeof(filename), "/data/misc/audioserver/debug_in_%p.pcm", (void*)in);
+
+ dump_states[index].fd = fopen(filename, "wb+");
+ if (dump_states[index].fd == NULL) {
+ ALOGD("DEBUG open %s ,errno = %s", filename, strerror(errno));
+ pthread_mutex_unlock(&dump_mutex);
+ return;
+ } else {
+ ALOGD("dump pcm to file %s", filename);
}
}
+
+ // 写入数据
+ ALOGD("dump in pcm %zu bytes to stream %p", bytes, (void*)in);
+ fwrite(buffer, bytes, 1, dump_states[index].fd);
+ dump_states[index].offset += bytes;
+ fflush(dump_states[index].fd);
+
+ // 检查是否达到大小限制
+ if (dump_states[index].offset >= size * 1024 * 1024) {
+ fclose(dump_states[index].fd);
+ dump_states[index].fd = NULL;
+ dump_states[index].offset = 0;
+ dump_states[index].stream_ptr = NULL;
+ property_set("vendor.audio.record.in", "0");
+ ALOGD("TEST record pcmfile end for stream %p", (void*)in);
+ }
+
+ pthread_mutex_unlock(&dump_mutex);
}
@@ -3224,8 +3279,6 @@ static ssize_t in_read(struct audio_stream_in *stream, void* buffer,
bytes = frames_rd * audio_stream_in_frame_size(stream);
}
- dump_in_data(buffer, bytes);
-
#ifdef AUDIO_3A
do {
if (adev->voice_api != NULL) {
@@ -3256,6 +3309,9 @@ exit:
usleep(bytes * 1000000 / audio_stream_in_frame_size(stream) /
in_get_sample_rate(&stream->common));
do_in_standby(in);
+ } else {
+ // 只有在成功读取数据时才dump
+ dump_in_data(buffer, bytes, in);
}
#if 0
if(in->device & AUDIO_DEVICE_IN_HDMI){//录制HDMI-IN的数据