Added option to specify a maximum file size when recording an AEC dump.

For applications with a strict filesize limit for debug files,
I added an option to specify a maximum filesize for AEC dumps. An
existing unit test is extended to check that the feature works as
advertised.

BUG=webrtc:4741
TBR=glaznev@webrtc.org

Review URL: https://codereview.webrtc.org/1413483003

Cr-Commit-Position: refs/heads/master@{#11081}
This commit is contained in:
ivoc
2015-12-18 03:53:37 -08:00
committed by Commit bot
parent 095ae15d6b
commit ae2c5ad12a
22 changed files with 136 additions and 60 deletions

View File

@ -1280,11 +1280,12 @@ JOW(jlong, PeerConnectionFactory_nativeCreateAudioTrack)(
} }
JOW(jboolean, PeerConnectionFactory_nativeStartAecDump)( JOW(jboolean, PeerConnectionFactory_nativeStartAecDump)(
JNIEnv* jni, jclass, jlong native_factory, jint file) { JNIEnv* jni, jclass, jlong native_factory, jint file,
jint filesize_limit_bytes) {
#if defined(ANDROID) #if defined(ANDROID)
rtc::scoped_refptr<PeerConnectionFactoryInterface> factory( rtc::scoped_refptr<PeerConnectionFactoryInterface> factory(
factoryFromJava(native_factory)); factoryFromJava(native_factory));
return factory->StartAecDump(file); return factory->StartAecDump(file, filesize_limit_bytes);
#else #else
return false; return false;
#endif #endif

View File

@ -143,8 +143,8 @@ public class PeerConnectionFactory {
// Starts recording an AEC dump. Ownership of the file is transfered to the // Starts recording an AEC dump. Ownership of the file is transfered to the
// native code. If an AEC dump is already in progress, it will be stopped and // native code. If an AEC dump is already in progress, it will be stopped and
// a new one will start using the provided file. // a new one will start using the provided file.
public boolean startAecDump(int file_descriptor) { public boolean startAecDump(int file_descriptor, int filesize_limit_bytes) {
return nativeStartAecDump(nativeFactory, file_descriptor); return nativeStartAecDump(nativeFactory, file_descriptor, filesize_limit_bytes);
} }
// Stops recording an AEC dump. If no AEC dump is currently being recorded, // Stops recording an AEC dump. If no AEC dump is currently being recorded,
@ -250,7 +250,8 @@ public class PeerConnectionFactory {
private static native long nativeCreateAudioTrack( private static native long nativeCreateAudioTrack(
long nativeFactory, String id, long nativeSource); long nativeFactory, String id, long nativeSource);
private static native boolean nativeStartAecDump(long nativeFactory, int file_descriptor); private static native boolean nativeStartAecDump(
long nativeFactory, int file_descriptor, int filesize_limit_bytes);
private static native void nativeStopAecDump(long nativeFactory); private static native void nativeStopAecDump(long nativeFactory);

View File

@ -232,9 +232,10 @@ PeerConnectionFactory::CreateVideoSource(
return VideoSourceProxy::Create(signaling_thread_, source); return VideoSourceProxy::Create(signaling_thread_, source);
} }
bool PeerConnectionFactory::StartAecDump(rtc::PlatformFile file) { bool PeerConnectionFactory::StartAecDump(rtc::PlatformFile file,
int64_t max_size_bytes) {
RTC_DCHECK(signaling_thread_->IsCurrent()); RTC_DCHECK(signaling_thread_->IsCurrent());
return channel_manager_->StartAecDump(file); return channel_manager_->StartAecDump(file, max_size_bytes);
} }
void PeerConnectionFactory::StopAecDump() { void PeerConnectionFactory::StopAecDump() {

View File

@ -93,7 +93,7 @@ class PeerConnectionFactory : public PeerConnectionFactoryInterface {
CreateAudioTrack(const std::string& id, CreateAudioTrack(const std::string& id,
AudioSourceInterface* audio_source) override; AudioSourceInterface* audio_source) override;
bool StartAecDump(rtc::PlatformFile file) override; bool StartAecDump(rtc::PlatformFile file, int64_t max_size_bytes) override;
void StopAecDump() override; void StopAecDump() override;
bool StartRtcEventLog(rtc::PlatformFile file) override; bool StartRtcEventLog(rtc::PlatformFile file) override;
void StopRtcEventLog() override; void StopRtcEventLog() override;

View File

@ -72,7 +72,7 @@ BEGIN_PROXY_MAP(PeerConnectionFactory)
CreateVideoTrack, const std::string&, VideoSourceInterface*) CreateVideoTrack, const std::string&, VideoSourceInterface*)
PROXY_METHOD2(rtc::scoped_refptr<AudioTrackInterface>, PROXY_METHOD2(rtc::scoped_refptr<AudioTrackInterface>,
CreateAudioTrack, const std::string&, AudioSourceInterface*) CreateAudioTrack, const std::string&, AudioSourceInterface*)
PROXY_METHOD1(bool, StartAecDump, rtc::PlatformFile) PROXY_METHOD2(bool, StartAecDump, rtc::PlatformFile, int64_t)
PROXY_METHOD0(void, StopAecDump) PROXY_METHOD0(void, StopAecDump)
PROXY_METHOD1(bool, StartRtcEventLog, rtc::PlatformFile) PROXY_METHOD1(bool, StartRtcEventLog, rtc::PlatformFile)
PROXY_METHOD0(void, StopRtcEventLog) PROXY_METHOD0(void, StopRtcEventLog)

View File

@ -637,9 +637,11 @@ class PeerConnectionFactoryInterface : public rtc::RefCountInterface {
// Starts AEC dump using existing file. Takes ownership of |file| and passes // Starts AEC dump using existing file. Takes ownership of |file| and passes
// it on to VoiceEngine (via other objects) immediately, which will take // it on to VoiceEngine (via other objects) immediately, which will take
// the ownerhip. If the operation fails, the file will be closed. // the ownerhip. If the operation fails, the file will be closed.
// TODO(grunell): Remove when Chromium has started to use AEC in each source. // A maximum file size in bytes can be specified. When the file size limit is
// http://crbug.com/264611. // reached, logging is stopped automatically. If max_size_bytes is set to a
virtual bool StartAecDump(rtc::PlatformFile file) = 0; // value <= 0, no limit will be used, and logging will continue until the
// StopAecDump function is called.
virtual bool StartAecDump(rtc::PlatformFile file, int64_t max_size_bytes) = 0;
// Stops logging the AEC dump. // Stops logging the AEC dump.
virtual void StopAecDump() = 0; virtual void StopAecDump() = 0;

View File

@ -762,7 +762,9 @@ class FakeVoiceEngine : public FakeBaseEngine {
int GetInputLevel() { return 0; } int GetInputLevel() { return 0; }
bool StartAecDump(rtc::PlatformFile file) { return false; } bool StartAecDump(rtc::PlatformFile file, int64_t max_size_bytes) {
return false;
}
void StopAecDump() {} void StopAecDump() {}

View File

@ -102,8 +102,10 @@ class MediaEngineInterface {
virtual const std::vector<VideoCodec>& video_codecs() = 0; virtual const std::vector<VideoCodec>& video_codecs() = 0;
virtual RtpCapabilities GetVideoCapabilities() = 0; virtual RtpCapabilities GetVideoCapabilities() = 0;
// Starts AEC dump using existing file. // Starts AEC dump using existing file, a maximum file size in bytes can be
virtual bool StartAecDump(rtc::PlatformFile file) = 0; // specified. Logging is stopped just before the size limit is exceeded.
// If max_size_bytes is set to a value <= 0, no limit will be used.
virtual bool StartAecDump(rtc::PlatformFile file, int64_t max_size_bytes) = 0;
// Stops recording AEC dump. // Stops recording AEC dump.
virtual void StopAecDump() = 0; virtual void StopAecDump() = 0;
@ -185,8 +187,8 @@ class CompositeMediaEngine : public MediaEngineInterface {
return video_.GetCapabilities(); return video_.GetCapabilities();
} }
virtual bool StartAecDump(rtc::PlatformFile file) { virtual bool StartAecDump(rtc::PlatformFile file, int64_t max_size_bytes) {
return voice_.StartAecDump(file); return voice_.StartAecDump(file, max_size_bytes);
} }
virtual void StopAecDump() { virtual void StopAecDump() {

View File

@ -113,8 +113,9 @@ class FakeAudioProcessing : public webrtc::AudioProcessing {
WEBRTC_VOID_STUB(set_stream_key_pressed, (bool key_pressed)); WEBRTC_VOID_STUB(set_stream_key_pressed, (bool key_pressed));
WEBRTC_VOID_STUB(set_delay_offset_ms, (int offset)); WEBRTC_VOID_STUB(set_delay_offset_ms, (int offset));
WEBRTC_STUB_CONST(delay_offset_ms, ()); WEBRTC_STUB_CONST(delay_offset_ms, ());
WEBRTC_STUB(StartDebugRecording, (const char filename[kMaxFilenameSize])); WEBRTC_STUB(StartDebugRecording,
WEBRTC_STUB(StartDebugRecording, (FILE* handle)); (const char filename[kMaxFilenameSize], int64_t max_size_bytes));
WEBRTC_STUB(StartDebugRecording, (FILE * handle, int64_t max_size_bytes));
WEBRTC_STUB(StopDebugRecording, ()); WEBRTC_STUB(StopDebugRecording, ());
WEBRTC_VOID_STUB(UpdateHistogramsOnCallEnd, ()); WEBRTC_VOID_STUB(UpdateHistogramsOnCallEnd, ());
webrtc::EchoCancellation* echo_cancellation() const override { return NULL; } webrtc::EchoCancellation* echo_cancellation() const override { return NULL; }

View File

@ -1011,7 +1011,8 @@ bool WebRtcVoiceEngine::SetAudioDeviceModule(webrtc::AudioDeviceModule* adm) {
return true; return true;
} }
bool WebRtcVoiceEngine::StartAecDump(rtc::PlatformFile file) { bool WebRtcVoiceEngine::StartAecDump(rtc::PlatformFile file,
int64_t max_size_bytes) {
RTC_DCHECK(worker_thread_checker_.CalledOnValidThread()); RTC_DCHECK(worker_thread_checker_.CalledOnValidThread());
FILE* aec_dump_file_stream = rtc::FdopenPlatformFileForWriting(file); FILE* aec_dump_file_stream = rtc::FdopenPlatformFileForWriting(file);
if (!aec_dump_file_stream) { if (!aec_dump_file_stream) {
@ -1021,7 +1022,8 @@ bool WebRtcVoiceEngine::StartAecDump(rtc::PlatformFile file) {
return false; return false;
} }
StopAecDump(); StopAecDump();
if (voe_wrapper_->processing()->StartDebugRecording(aec_dump_file_stream) != if (voe_wrapper_->base()->audio_processing()->StartDebugRecording(
aec_dump_file_stream, max_size_bytes) !=
webrtc::AudioProcessing::kNoError) { webrtc::AudioProcessing::kNoError) {
LOG_RTCERR0(StartDebugRecording); LOG_RTCERR0(StartDebugRecording);
fclose(aec_dump_file_stream); fclose(aec_dump_file_stream);
@ -1035,8 +1037,8 @@ void WebRtcVoiceEngine::StartAecDump(const std::string& filename) {
RTC_DCHECK(worker_thread_checker_.CalledOnValidThread()); RTC_DCHECK(worker_thread_checker_.CalledOnValidThread());
if (!is_dumping_aec_) { if (!is_dumping_aec_) {
// Start dumping AEC when we are not dumping. // Start dumping AEC when we are not dumping.
if (voe_wrapper_->processing()->StartDebugRecording( if (voe_wrapper_->base()->audio_processing()->StartDebugRecording(
filename.c_str()) != webrtc::AudioProcessing::kNoError) { filename.c_str(), -1) != webrtc::AudioProcessing::kNoError) {
LOG_RTCERR1(StartDebugRecording, filename.c_str()); LOG_RTCERR1(StartDebugRecording, filename.c_str());
} else { } else {
is_dumping_aec_ = true; is_dumping_aec_ = true;
@ -1048,7 +1050,7 @@ void WebRtcVoiceEngine::StopAecDump() {
RTC_DCHECK(worker_thread_checker_.CalledOnValidThread()); RTC_DCHECK(worker_thread_checker_.CalledOnValidThread());
if (is_dumping_aec_) { if (is_dumping_aec_) {
// Stop dumping AEC when we are dumping. // Stop dumping AEC when we are dumping.
if (voe_wrapper_->processing()->StopDebugRecording() != if (voe_wrapper_->base()->audio_processing()->StopDebugRecording() !=
webrtc::AudioProcessing::kNoError) { webrtc::AudioProcessing::kNoError) {
LOG_RTCERR0(StopDebugRecording); LOG_RTCERR0(StopDebugRecording);
} }

View File

@ -94,8 +94,11 @@ class WebRtcVoiceEngine final : public webrtc::TraceCallback {
// Set the external ADM. This can only be called before Init. // Set the external ADM. This can only be called before Init.
bool SetAudioDeviceModule(webrtc::AudioDeviceModule* adm); bool SetAudioDeviceModule(webrtc::AudioDeviceModule* adm);
// Starts AEC dump using existing file. // Starts AEC dump using an existing file. A maximum file size in bytes can be
bool StartAecDump(rtc::PlatformFile file); // specified. When the maximum file size is reached, logging is stopped and
// the file is closed. If max_size_bytes is set to <= 0, no limit will be
// used.
bool StartAecDump(rtc::PlatformFile file, int64_t max_size_bytes);
// Stops AEC dump. // Stops AEC dump.
void StopAecDump(); void StopAecDump();

View File

@ -550,9 +550,11 @@ void ChannelManager::OnMessage(rtc::Message* message) {
} }
} }
bool ChannelManager::StartAecDump(rtc::PlatformFile file) { bool ChannelManager::StartAecDump(rtc::PlatformFile file,
return worker_thread_->Invoke<bool>( int64_t max_size_bytes) {
Bind(&MediaEngineInterface::StartAecDump, media_engine_.get(), file)); return worker_thread_->Invoke<bool>(Bind(&MediaEngineInterface::StartAecDump,
media_engine_.get(), file,
max_size_bytes));
} }
void ChannelManager::StopAecDump() { void ChannelManager::StopAecDump() {

View File

@ -162,8 +162,10 @@ class ChannelManager : public rtc::MessageHandler,
// The operations below occur on the main thread. // The operations below occur on the main thread.
// Starts AEC dump using existing file. // Starts AEC dump using existing file, with a specified maximum file size in
bool StartAecDump(rtc::PlatformFile file); // bytes. When the limit is reached, logging will stop and the file will be
// closed. If max_size_bytes is set to <= 0, no limit will be used.
bool StartAecDump(rtc::PlatformFile file, int64_t max_size_bytes);
// Stops recording AEC dump. // Stops recording AEC dump.
void StopAecDump(); void StopAecDump();

View File

@ -496,7 +496,7 @@ public class PeerConnectionClient {
ParcelFileDescriptor.MODE_READ_WRITE | ParcelFileDescriptor.MODE_READ_WRITE |
ParcelFileDescriptor.MODE_CREATE | ParcelFileDescriptor.MODE_CREATE |
ParcelFileDescriptor.MODE_TRUNCATE); ParcelFileDescriptor.MODE_TRUNCATE);
factory.startAecDump(aecDumpFileDescriptor.getFd()); factory.startAecDump(aecDumpFileDescriptor.getFd(), -1);
} catch(IOException e) { } catch(IOException e) {
Log.e(TAG, "Can not open aecdump file", e); Log.e(TAG, "Can not open aecdump file", e);
} }

View File

@ -632,6 +632,7 @@ int AudioProcessingImpl::ProcessStream(const float* const* src,
for (int i = 0; i < formats_.api_format.output_stream().num_channels(); ++i) for (int i = 0; i < formats_.api_format.output_stream().num_channels(); ++i)
msg->add_output_channel(dest[i], channel_size); msg->add_output_channel(dest[i], channel_size);
RETURN_ON_ERR(WriteMessageToDebugFile(debug_dump_.debug_file.get(), RETURN_ON_ERR(WriteMessageToDebugFile(debug_dump_.debug_file.get(),
&debug_dump_.num_bytes_left_for_log_,
&crit_debug_, &debug_dump_.capture)); &crit_debug_, &debug_dump_.capture));
} }
#endif #endif
@ -719,6 +720,7 @@ int AudioProcessingImpl::ProcessStream(AudioFrame* frame) {
sizeof(int16_t) * frame->samples_per_channel_ * frame->num_channels_; sizeof(int16_t) * frame->samples_per_channel_ * frame->num_channels_;
msg->set_output_data(frame->data_, data_size); msg->set_output_data(frame->data_, data_size);
RETURN_ON_ERR(WriteMessageToDebugFile(debug_dump_.debug_file.get(), RETURN_ON_ERR(WriteMessageToDebugFile(debug_dump_.debug_file.get(),
&debug_dump_.num_bytes_left_for_log_,
&crit_debug_, &debug_dump_.capture)); &crit_debug_, &debug_dump_.capture));
} }
#endif #endif
@ -886,6 +888,7 @@ int AudioProcessingImpl::AnalyzeReverseStreamLocked(
i < formats_.api_format.reverse_input_stream().num_channels(); ++i) i < formats_.api_format.reverse_input_stream().num_channels(); ++i)
msg->add_channel(src[i], channel_size); msg->add_channel(src[i], channel_size);
RETURN_ON_ERR(WriteMessageToDebugFile(debug_dump_.debug_file.get(), RETURN_ON_ERR(WriteMessageToDebugFile(debug_dump_.debug_file.get(),
&debug_dump_.num_bytes_left_for_log_,
&crit_debug_, &debug_dump_.render)); &crit_debug_, &debug_dump_.render));
} }
#endif #endif
@ -954,6 +957,7 @@ int AudioProcessingImpl::AnalyzeReverseStream(AudioFrame* frame) {
sizeof(int16_t) * frame->samples_per_channel_ * frame->num_channels_; sizeof(int16_t) * frame->samples_per_channel_ * frame->num_channels_;
msg->set_data(frame->data_, data_size); msg->set_data(frame->data_, data_size);
RETURN_ON_ERR(WriteMessageToDebugFile(debug_dump_.debug_file.get(), RETURN_ON_ERR(WriteMessageToDebugFile(debug_dump_.debug_file.get(),
&debug_dump_.num_bytes_left_for_log_,
&crit_debug_, &debug_dump_.render)); &crit_debug_, &debug_dump_.render));
} }
#endif #endif
@ -1039,7 +1043,8 @@ int AudioProcessingImpl::delay_offset_ms() const {
} }
int AudioProcessingImpl::StartDebugRecording( int AudioProcessingImpl::StartDebugRecording(
const char filename[AudioProcessing::kMaxFilenameSize]) { const char filename[AudioProcessing::kMaxFilenameSize],
int64_t max_log_size_bytes) {
// Run in a single-threaded manner. // Run in a single-threaded manner.
rtc::CritScope cs_render(&crit_render_); rtc::CritScope cs_render(&crit_render_);
rtc::CritScope cs_capture(&crit_capture_); rtc::CritScope cs_capture(&crit_capture_);
@ -1050,6 +1055,7 @@ int AudioProcessingImpl::StartDebugRecording(
} }
#ifdef WEBRTC_AUDIOPROC_DEBUG_DUMP #ifdef WEBRTC_AUDIOPROC_DEBUG_DUMP
debug_dump_.num_bytes_left_for_log_ = max_log_size_bytes;
// Stop any ongoing recording. // Stop any ongoing recording.
if (debug_dump_.debug_file->Open()) { if (debug_dump_.debug_file->Open()) {
if (debug_dump_.debug_file->CloseFile() == -1) { if (debug_dump_.debug_file->CloseFile() == -1) {
@ -1070,7 +1076,8 @@ int AudioProcessingImpl::StartDebugRecording(
#endif // WEBRTC_AUDIOPROC_DEBUG_DUMP #endif // WEBRTC_AUDIOPROC_DEBUG_DUMP
} }
int AudioProcessingImpl::StartDebugRecording(FILE* handle) { int AudioProcessingImpl::StartDebugRecording(FILE* handle,
int64_t max_log_size_bytes) {
// Run in a single-threaded manner. // Run in a single-threaded manner.
rtc::CritScope cs_render(&crit_render_); rtc::CritScope cs_render(&crit_render_);
rtc::CritScope cs_capture(&crit_capture_); rtc::CritScope cs_capture(&crit_capture_);
@ -1080,6 +1087,8 @@ int AudioProcessingImpl::StartDebugRecording(FILE* handle) {
} }
#ifdef WEBRTC_AUDIOPROC_DEBUG_DUMP #ifdef WEBRTC_AUDIOPROC_DEBUG_DUMP
debug_dump_.num_bytes_left_for_log_ = max_log_size_bytes;
// Stop any ongoing recording. // Stop any ongoing recording.
if (debug_dump_.debug_file->Open()) { if (debug_dump_.debug_file->Open()) {
if (debug_dump_.debug_file->CloseFile() == -1) { if (debug_dump_.debug_file->CloseFile() == -1) {
@ -1105,7 +1114,7 @@ int AudioProcessingImpl::StartDebugRecordingForPlatformFile(
rtc::CritScope cs_render(&crit_render_); rtc::CritScope cs_render(&crit_render_);
rtc::CritScope cs_capture(&crit_capture_); rtc::CritScope cs_capture(&crit_capture_);
FILE* stream = rtc::FdopenPlatformFileForWriting(handle); FILE* stream = rtc::FdopenPlatformFileForWriting(handle);
return StartDebugRecording(stream); return StartDebugRecording(stream, -1);
} }
int AudioProcessingImpl::StopDebugRecording() { int AudioProcessingImpl::StopDebugRecording() {
@ -1400,6 +1409,7 @@ void AudioProcessingImpl::UpdateHistogramsOnCallEnd() {
#ifdef WEBRTC_AUDIOPROC_DEBUG_DUMP #ifdef WEBRTC_AUDIOPROC_DEBUG_DUMP
int AudioProcessingImpl::WriteMessageToDebugFile( int AudioProcessingImpl::WriteMessageToDebugFile(
FileWrapper* debug_file, FileWrapper* debug_file,
int64_t* filesize_limit_bytes,
rtc::CriticalSection* crit_debug, rtc::CriticalSection* crit_debug,
ApmDebugDumpThreadState* debug_state) { ApmDebugDumpThreadState* debug_state) {
int32_t size = debug_state->event_msg->ByteSize(); int32_t size = debug_state->event_msg->ByteSize();
@ -1417,7 +1427,19 @@ int AudioProcessingImpl::WriteMessageToDebugFile(
{ {
// Ensure atomic writes of the message. // Ensure atomic writes of the message.
rtc::CritScope cs_capture(crit_debug); rtc::CritScope cs_debug(crit_debug);
RTC_DCHECK(debug_file->Open());
// Update the byte counter.
if (*filesize_limit_bytes >= 0) {
*filesize_limit_bytes -=
(sizeof(int32_t) + debug_state->event_str.length());
if (*filesize_limit_bytes < 0) {
// Not enough bytes are left to write this message, so stop logging.
debug_file->CloseFile();
return kNoError;
}
}
// Write message preceded by its size. // Write message preceded by its size.
if (!debug_file->Write(&size, sizeof(int32_t))) { if (!debug_file->Write(&size, sizeof(int32_t))) {
return kFileError; return kFileError;
@ -1452,6 +1474,7 @@ int AudioProcessingImpl::WriteInitMessage() {
// debug_dump_.capture.event_msg. // debug_dump_.capture.event_msg.
RETURN_ON_ERR(WriteMessageToDebugFile(debug_dump_.debug_file.get(), RETURN_ON_ERR(WriteMessageToDebugFile(debug_dump_.debug_file.get(),
&debug_dump_.num_bytes_left_for_log_,
&crit_debug_, &debug_dump_.capture)); &crit_debug_, &debug_dump_.capture));
return kNoError; return kNoError;
} }
@ -1504,6 +1527,7 @@ int AudioProcessingImpl::WriteConfigMessage(bool forced) {
debug_dump_.capture.event_msg->mutable_config()->CopyFrom(config); debug_dump_.capture.event_msg->mutable_config()->CopyFrom(config);
RETURN_ON_ERR(WriteMessageToDebugFile(debug_dump_.debug_file.get(), RETURN_ON_ERR(WriteMessageToDebugFile(debug_dump_.debug_file.get(),
&debug_dump_.num_bytes_left_for_log_,
&crit_debug_, &debug_dump_.capture)); &crit_debug_, &debug_dump_.capture));
return kNoError; return kNoError;
} }

View File

@ -57,8 +57,9 @@ class AudioProcessingImpl : public AudioProcessing {
int Initialize(const ProcessingConfig& processing_config) override; int Initialize(const ProcessingConfig& processing_config) override;
void SetExtraOptions(const Config& config) override; void SetExtraOptions(const Config& config) override;
void UpdateHistogramsOnCallEnd() override; void UpdateHistogramsOnCallEnd() override;
int StartDebugRecording(const char filename[kMaxFilenameSize]) override; int StartDebugRecording(const char filename[kMaxFilenameSize],
int StartDebugRecording(FILE* handle) override; int64_t max_log_size_bytes) override;
int StartDebugRecording(FILE* handle, int64_t max_log_size_bytes) override;
int StartDebugRecordingForPlatformFile(rtc::PlatformFile handle) override; int StartDebugRecordingForPlatformFile(rtc::PlatformFile handle) override;
int StopDebugRecording() override; int StopDebugRecording() override;
@ -143,6 +144,9 @@ class AudioProcessingImpl : public AudioProcessing {
struct ApmDebugDumpState { struct ApmDebugDumpState {
ApmDebugDumpState() : debug_file(FileWrapper::Create()) {} ApmDebugDumpState() : debug_file(FileWrapper::Create()) {}
// Number of bytes that can still be written to the log before the maximum
// size is reached. A value of <= 0 indicates that no limit is used.
int64_t num_bytes_left_for_log_ = -1;
rtc::scoped_ptr<FileWrapper> debug_file; rtc::scoped_ptr<FileWrapper> debug_file;
ApmDebugDumpThreadState render; ApmDebugDumpThreadState render;
ApmDebugDumpThreadState capture; ApmDebugDumpThreadState capture;
@ -221,6 +225,7 @@ class AudioProcessingImpl : public AudioProcessing {
// TODO(andrew): make this more graceful. Ideally we would split this stuff // TODO(andrew): make this more graceful. Ideally we would split this stuff
// out into a separate class with an "enabled" and "disabled" implementation. // out into a separate class with an "enabled" and "disabled" implementation.
static int WriteMessageToDebugFile(FileWrapper* debug_file, static int WriteMessageToDebugFile(FileWrapper* debug_file,
int64_t* filesize_limit_bytes,
rtc::CriticalSection* crit_debug, rtc::CriticalSection* crit_debug,
ApmDebugDumpThreadState* debug_state); ApmDebugDumpThreadState* debug_state);
int WriteInitMessage() EXCLUSIVE_LOCKS_REQUIRED(crit_render_, crit_capture_); int WriteInitMessage() EXCLUSIVE_LOCKS_REQUIRED(crit_render_, crit_capture_);

View File

@ -408,13 +408,17 @@ class AudioProcessing {
// Starts recording debugging information to a file specified by |filename|, // Starts recording debugging information to a file specified by |filename|,
// a NULL-terminated string. If there is an ongoing recording, the old file // a NULL-terminated string. If there is an ongoing recording, the old file
// will be closed, and recording will continue in the newly specified file. // will be closed, and recording will continue in the newly specified file.
// An already existing file will be overwritten without warning. // An already existing file will be overwritten without warning. A maximum
// file size (in bytes) for the log can be specified. The logging is stopped
// once the limit has been reached. If max_log_size_bytes is set to a value
// <= 0, no limit will be used.
static const size_t kMaxFilenameSize = 1024; static const size_t kMaxFilenameSize = 1024;
virtual int StartDebugRecording(const char filename[kMaxFilenameSize]) = 0; virtual int StartDebugRecording(const char filename[kMaxFilenameSize],
int64_t max_log_size_bytes) = 0;
// Same as above but uses an existing file handle. Takes ownership // Same as above but uses an existing file handle. Takes ownership
// of |handle| and closes it at StopDebugRecording(). // of |handle| and closes it at StopDebugRecording().
virtual int StartDebugRecording(FILE* handle) = 0; virtual int StartDebugRecording(FILE* handle, int64_t max_log_size_bytes) = 0;
// Same as above but uses an existing PlatformFile handle. Takes ownership // Same as above but uses an existing PlatformFile handle. Takes ownership
// of |handle| and closes it at StopDebugRecording(). // of |handle| and closes it at StopDebugRecording().

View File

@ -250,10 +250,11 @@ class MockAudioProcessing : public AudioProcessing {
void(int offset)); void(int offset));
MOCK_CONST_METHOD0(delay_offset_ms, MOCK_CONST_METHOD0(delay_offset_ms,
int()); int());
MOCK_METHOD1(StartDebugRecording, MOCK_METHOD2(StartDebugRecording,
int(const char filename[kMaxFilenameSize])); int(const char filename[kMaxFilenameSize],
MOCK_METHOD1(StartDebugRecording, int64_t max_log_size_bytes));
int(FILE* handle)); MOCK_METHOD2(StartDebugRecording,
int(FILE* handle, int64_t max_log_size_bytes));
MOCK_METHOD0(StopDebugRecording, MOCK_METHOD0(StopDebugRecording,
int()); int());
MOCK_METHOD0(UpdateHistogramsOnCallEnd, void()); MOCK_METHOD0(UpdateHistogramsOnCallEnd, void());

View File

@ -388,7 +388,8 @@ class ApmTest : public ::testing::Test {
int AnalyzeReverseStreamChooser(Format format); int AnalyzeReverseStreamChooser(Format format);
void ProcessDebugDump(const std::string& in_filename, void ProcessDebugDump(const std::string& in_filename,
const std::string& out_filename, const std::string& out_filename,
Format format); Format format,
int max_size_bytes);
void VerifyDebugDumpTest(Format format); void VerifyDebugDumpTest(Format format);
const std::string output_path_; const std::string output_path_;
@ -1711,7 +1712,8 @@ TEST_F(ApmTest, SplittingFilter) {
#ifdef WEBRTC_AUDIOPROC_DEBUG_DUMP #ifdef WEBRTC_AUDIOPROC_DEBUG_DUMP
void ApmTest::ProcessDebugDump(const std::string& in_filename, void ApmTest::ProcessDebugDump(const std::string& in_filename,
const std::string& out_filename, const std::string& out_filename,
Format format) { Format format,
int max_size_bytes) {
FILE* in_file = fopen(in_filename.c_str(), "rb"); FILE* in_file = fopen(in_filename.c_str(), "rb");
ASSERT_TRUE(in_file != NULL); ASSERT_TRUE(in_file != NULL);
audioproc::Event event_msg; audioproc::Event event_msg;
@ -1739,7 +1741,8 @@ void ApmTest::ProcessDebugDump(const std::string& in_filename,
if (first_init) { if (first_init) {
// StartDebugRecording() writes an additional init message. Don't start // StartDebugRecording() writes an additional init message. Don't start
// recording until after the first init to avoid the extra message. // recording until after the first init to avoid the extra message.
EXPECT_NOERR(apm_->StartDebugRecording(out_filename.c_str())); EXPECT_NOERR(
apm_->StartDebugRecording(out_filename.c_str(), max_size_bytes));
first_init = false; first_init = false;
} }
@ -1812,34 +1815,54 @@ void ApmTest::VerifyDebugDumpTest(Format format) {
test::OutputPath(), std::string("ref") + format_string + "_aecdump"); test::OutputPath(), std::string("ref") + format_string + "_aecdump");
const std::string out_filename = test::TempFilename( const std::string out_filename = test::TempFilename(
test::OutputPath(), std::string("out") + format_string + "_aecdump"); test::OutputPath(), std::string("out") + format_string + "_aecdump");
const std::string limited_filename = test::TempFilename(
test::OutputPath(), std::string("limited") + format_string + "_aecdump");
const size_t logging_limit_bytes = 100000;
// We expect at least this many bytes in the created logfile.
const size_t logging_expected_bytes = 95000;
EnableAllComponents(); EnableAllComponents();
ProcessDebugDump(in_filename, ref_filename, format); ProcessDebugDump(in_filename, ref_filename, format, -1);
ProcessDebugDump(ref_filename, out_filename, format); ProcessDebugDump(ref_filename, out_filename, format, -1);
ProcessDebugDump(ref_filename, limited_filename, format, logging_limit_bytes);
FILE* ref_file = fopen(ref_filename.c_str(), "rb"); FILE* ref_file = fopen(ref_filename.c_str(), "rb");
FILE* out_file = fopen(out_filename.c_str(), "rb"); FILE* out_file = fopen(out_filename.c_str(), "rb");
FILE* limited_file = fopen(limited_filename.c_str(), "rb");
ASSERT_TRUE(ref_file != NULL); ASSERT_TRUE(ref_file != NULL);
ASSERT_TRUE(out_file != NULL); ASSERT_TRUE(out_file != NULL);
ASSERT_TRUE(limited_file != NULL);
rtc::scoped_ptr<uint8_t[]> ref_bytes; rtc::scoped_ptr<uint8_t[]> ref_bytes;
rtc::scoped_ptr<uint8_t[]> out_bytes; rtc::scoped_ptr<uint8_t[]> out_bytes;
rtc::scoped_ptr<uint8_t[]> limited_bytes;
size_t ref_size = ReadMessageBytesFromFile(ref_file, &ref_bytes); size_t ref_size = ReadMessageBytesFromFile(ref_file, &ref_bytes);
size_t out_size = ReadMessageBytesFromFile(out_file, &out_bytes); size_t out_size = ReadMessageBytesFromFile(out_file, &out_bytes);
size_t limited_size = ReadMessageBytesFromFile(limited_file, &limited_bytes);
size_t bytes_read = 0; size_t bytes_read = 0;
size_t bytes_read_limited = 0;
while (ref_size > 0 && out_size > 0) { while (ref_size > 0 && out_size > 0) {
bytes_read += ref_size; bytes_read += ref_size;
bytes_read_limited += limited_size;
EXPECT_EQ(ref_size, out_size); EXPECT_EQ(ref_size, out_size);
EXPECT_GE(ref_size, limited_size);
EXPECT_EQ(0, memcmp(ref_bytes.get(), out_bytes.get(), ref_size)); EXPECT_EQ(0, memcmp(ref_bytes.get(), out_bytes.get(), ref_size));
EXPECT_EQ(0, memcmp(ref_bytes.get(), limited_bytes.get(), limited_size));
ref_size = ReadMessageBytesFromFile(ref_file, &ref_bytes); ref_size = ReadMessageBytesFromFile(ref_file, &ref_bytes);
out_size = ReadMessageBytesFromFile(out_file, &out_bytes); out_size = ReadMessageBytesFromFile(out_file, &out_bytes);
limited_size = ReadMessageBytesFromFile(limited_file, &limited_bytes);
} }
EXPECT_GT(bytes_read, 0u); EXPECT_GT(bytes_read, 0u);
EXPECT_GT(bytes_read_limited, logging_expected_bytes);
EXPECT_LE(bytes_read_limited, logging_limit_bytes);
EXPECT_NE(0, feof(ref_file)); EXPECT_NE(0, feof(ref_file));
EXPECT_NE(0, feof(out_file)); EXPECT_NE(0, feof(out_file));
EXPECT_NE(0, feof(limited_file));
ASSERT_EQ(0, fclose(ref_file)); ASSERT_EQ(0, fclose(ref_file));
ASSERT_EQ(0, fclose(out_file)); ASSERT_EQ(0, fclose(out_file));
ASSERT_EQ(0, fclose(limited_file));
remove(ref_filename.c_str()); remove(ref_filename.c_str());
remove(out_filename.c_str()); remove(out_filename.c_str());
remove(limited_filename.c_str());
} }
TEST_F(ApmTest, VerifyDebugDumpInt) { TEST_F(ApmTest, VerifyDebugDumpInt) {
@ -1856,13 +1879,13 @@ TEST_F(ApmTest, DebugDump) {
const std::string filename = const std::string filename =
test::TempFilename(test::OutputPath(), "debug_aec"); test::TempFilename(test::OutputPath(), "debug_aec");
EXPECT_EQ(apm_->kNullPointerError, EXPECT_EQ(apm_->kNullPointerError,
apm_->StartDebugRecording(static_cast<const char*>(NULL))); apm_->StartDebugRecording(static_cast<const char*>(NULL), -1));
#ifdef WEBRTC_AUDIOPROC_DEBUG_DUMP #ifdef WEBRTC_AUDIOPROC_DEBUG_DUMP
// Stopping without having started should be OK. // Stopping without having started should be OK.
EXPECT_EQ(apm_->kNoError, apm_->StopDebugRecording()); EXPECT_EQ(apm_->kNoError, apm_->StopDebugRecording());
EXPECT_EQ(apm_->kNoError, apm_->StartDebugRecording(filename.c_str())); EXPECT_EQ(apm_->kNoError, apm_->StartDebugRecording(filename.c_str(), -1));
EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_)); EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_));
EXPECT_EQ(apm_->kNoError, apm_->AnalyzeReverseStream(revframe_)); EXPECT_EQ(apm_->kNoError, apm_->AnalyzeReverseStream(revframe_));
EXPECT_EQ(apm_->kNoError, apm_->StopDebugRecording()); EXPECT_EQ(apm_->kNoError, apm_->StopDebugRecording());
@ -1876,7 +1899,7 @@ TEST_F(ApmTest, DebugDump) {
ASSERT_EQ(0, remove(filename.c_str())); ASSERT_EQ(0, remove(filename.c_str()));
#else #else
EXPECT_EQ(apm_->kUnsupportedFunctionError, EXPECT_EQ(apm_->kUnsupportedFunctionError,
apm_->StartDebugRecording(filename.c_str())); apm_->StartDebugRecording(filename.c_str(), -1));
EXPECT_EQ(apm_->kUnsupportedFunctionError, apm_->StopDebugRecording()); EXPECT_EQ(apm_->kUnsupportedFunctionError, apm_->StopDebugRecording());
// Verify the file has NOT been written. // Verify the file has NOT been written.
@ -1887,7 +1910,7 @@ TEST_F(ApmTest, DebugDump) {
// TODO(andrew): expand test to verify output. // TODO(andrew): expand test to verify output.
TEST_F(ApmTest, DebugDumpFromFileHandle) { TEST_F(ApmTest, DebugDumpFromFileHandle) {
FILE* fid = NULL; FILE* fid = NULL;
EXPECT_EQ(apm_->kNullPointerError, apm_->StartDebugRecording(fid)); EXPECT_EQ(apm_->kNullPointerError, apm_->StartDebugRecording(fid, -1));
const std::string filename = const std::string filename =
test::TempFilename(test::OutputPath(), "debug_aec"); test::TempFilename(test::OutputPath(), "debug_aec");
fid = fopen(filename.c_str(), "w"); fid = fopen(filename.c_str(), "w");
@ -1897,7 +1920,7 @@ TEST_F(ApmTest, DebugDumpFromFileHandle) {
// Stopping without having started should be OK. // Stopping without having started should be OK.
EXPECT_EQ(apm_->kNoError, apm_->StopDebugRecording()); EXPECT_EQ(apm_->kNoError, apm_->StopDebugRecording());
EXPECT_EQ(apm_->kNoError, apm_->StartDebugRecording(fid)); EXPECT_EQ(apm_->kNoError, apm_->StartDebugRecording(fid, -1));
EXPECT_EQ(apm_->kNoError, apm_->AnalyzeReverseStream(revframe_)); EXPECT_EQ(apm_->kNoError, apm_->AnalyzeReverseStream(revframe_));
EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_)); EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_));
EXPECT_EQ(apm_->kNoError, apm_->StopDebugRecording()); EXPECT_EQ(apm_->kNoError, apm_->StopDebugRecording());
@ -1911,7 +1934,7 @@ TEST_F(ApmTest, DebugDumpFromFileHandle) {
ASSERT_EQ(0, remove(filename.c_str())); ASSERT_EQ(0, remove(filename.c_str()));
#else #else
EXPECT_EQ(apm_->kUnsupportedFunctionError, EXPECT_EQ(apm_->kUnsupportedFunctionError,
apm_->StartDebugRecording(fid)); apm_->StartDebugRecording(fid, -1));
EXPECT_EQ(apm_->kUnsupportedFunctionError, apm_->StopDebugRecording()); EXPECT_EQ(apm_->kUnsupportedFunctionError, apm_->StopDebugRecording());
ASSERT_EQ(0, fclose(fid)); ASSERT_EQ(0, fclose(fid));

View File

@ -181,7 +181,7 @@ void DebugDumpGenerator::SetOutputChannels(int channels) {
} }
void DebugDumpGenerator::StartRecording() { void DebugDumpGenerator::StartRecording() {
apm_->StartDebugRecording(dump_file_name_.c_str()); apm_->StartDebugRecording(dump_file_name_.c_str(), -1);
} }
void DebugDumpGenerator::Process(size_t num_blocks) { void DebugDumpGenerator::Process(size_t num_blocks) {

View File

@ -434,7 +434,7 @@ void void_main(int argc, char* argv[]) {
} else if (strcmp(argv[i], "--debug_file") == 0) { } else if (strcmp(argv[i], "--debug_file") == 0) {
i++; i++;
ASSERT_LT(i, argc) << "Specify filename after --debug_file"; ASSERT_LT(i, argc) << "Specify filename after --debug_file";
ASSERT_EQ(apm->kNoError, apm->StartDebugRecording(argv[i])); ASSERT_EQ(apm->kNoError, apm->StartDebugRecording(argv[i], -1));
} else { } else {
FAIL() << "Unrecognized argument " << argv[i]; FAIL() << "Unrecognized argument " << argv[i];
} }

View File

@ -924,7 +924,7 @@ int VoEAudioProcessingImpl::StartDebugRecording(const char* fileNameUTF8) {
return -1; return -1;
} }
return _shared->audio_processing()->StartDebugRecording(fileNameUTF8); return _shared->audio_processing()->StartDebugRecording(fileNameUTF8, -1);
} }
int VoEAudioProcessingImpl::StartDebugRecording(FILE* file_handle) { int VoEAudioProcessingImpl::StartDebugRecording(FILE* file_handle) {
@ -935,7 +935,7 @@ int VoEAudioProcessingImpl::StartDebugRecording(FILE* file_handle) {
return -1; return -1;
} }
return _shared->audio_processing()->StartDebugRecording(file_handle); return _shared->audio_processing()->StartDebugRecording(file_handle, -1);
} }
int VoEAudioProcessingImpl::StopDebugRecording() { int VoEAudioProcessingImpl::StopDebugRecording() {