【android 12】【audio】dump系统音频流中各个阶段的音频数据

【android 12】【audio】dump系统音频流中各个阶段的音频数据

一、概述

对于音频数据问题,如音效、重音问题,在不确定问题出现在那个阶段时,可以把各个阶段的音频数据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& audioFlinger, audio

// 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(buffer);

+ 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(buffer);

+ 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& args __unused) { }

+ // 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 thread = mThread.promote();

+ 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 thread = mThread.promote();

+ 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 thread = mThread.promote();

+ 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的数据


相关推荐

亚洲大象是怎么繁殖的?
串珠葫芦教程
CPU里真的有黄金:看完秒懂
叮咚买菜官方网站
世界杯冠军为什么难卫冕 从猎人变成猎物后成众矢之的
海大大蒸汽海鲜主题餐厅(欢乐颂店)