diff --git a/webrtc/modules/audio_device/android/opensles_player.cc b/webrtc/modules/audio_device/android/opensles_player.cc index 7dfc5ec891..2d305f0ff7 100644 --- a/webrtc/modules/audio_device/android/opensles_player.cc +++ b/webrtc/modules/audio_device/android/opensles_player.cc @@ -205,19 +205,16 @@ void OpenSLESPlayer::AllocateDataBuffers() { // recommended to construct audio buffers so that they contain an exact // multiple of this number. If so, callbacks will occur at regular intervals, // which reduces jitter. - ALOGD("native buffer size: %" PRIuS, audio_parameters_.GetBytesPerBuffer()); + const size_t buffer_size_in_bytes = audio_parameters_.GetBytesPerBuffer(); + ALOGD("native buffer size: %" PRIuS, buffer_size_in_bytes); ALOGD("native buffer size in ms: %.2f", audio_parameters_.GetBufferSizeInMilliseconds()); - fine_audio_buffer_.reset(new FineAudioBuffer( - audio_device_buffer_, audio_parameters_.GetBytesPerBuffer(), - audio_parameters_.sample_rate())); - // Each buffer must be of this size to avoid unnecessary memcpy while caching - // data between successive callbacks. - const size_t required_buffer_size = - fine_audio_buffer_->RequiredPlayoutBufferSizeBytes(); - ALOGD("required buffer size: %" PRIuS, required_buffer_size); + fine_audio_buffer_.reset( + new FineAudioBuffer(audio_device_buffer_, buffer_size_in_bytes, + audio_parameters_.sample_rate())); + // Allocated memory for audio buffers. for (int i = 0; i < kNumOfOpenSLESBuffers; ++i) { - audio_buffers_[i].reset(new SLint8[required_buffer_size]); + audio_buffers_[i].reset(new SLint8[buffer_size_in_bytes]); } } diff --git a/webrtc/modules/audio_device/fine_audio_buffer.cc b/webrtc/modules/audio_device/fine_audio_buffer.cc index 0829e98203..83775741d8 100644 --- a/webrtc/modules/audio_device/fine_audio_buffer.cc +++ b/webrtc/modules/audio_device/fine_audio_buffer.cc @@ -27,25 +27,14 @@ FineAudioBuffer::FineAudioBuffer(AudioDeviceBuffer* device_buffer, desired_frame_size_bytes_(desired_frame_size_bytes), sample_rate_(sample_rate), samples_per_10_ms_(static_cast(sample_rate_ * 10 / 1000)), - bytes_per_10_ms_(samples_per_10_ms_ * sizeof(int16_t)), - playout_cached_buffer_start_(0), - playout_cached_bytes_(0) { - playout_cache_buffer_.reset(new int8_t[bytes_per_10_ms_]); + bytes_per_10_ms_(samples_per_10_ms_ * sizeof(int16_t)) { + LOG(INFO) << "desired_frame_size_bytes:" << desired_frame_size_bytes; } FineAudioBuffer::~FineAudioBuffer() {} -size_t FineAudioBuffer::RequiredPlayoutBufferSizeBytes() { - // It is possible that we store the desired frame size - 1 samples. Since new - // audio frames are pulled in chunks of 10ms we will need a buffer that can - // hold desired_frame_size - 1 + 10ms of data. We omit the - 1. - return desired_frame_size_bytes_ + bytes_per_10_ms_; -} - void FineAudioBuffer::ResetPlayout() { - playout_cached_buffer_start_ = 0; - playout_cached_bytes_ = 0; - memset(playout_cache_buffer_.get(), 0, bytes_per_10_ms_); + playout_buffer_.Clear(); } void FineAudioBuffer::ResetRecord() { @@ -53,50 +42,30 @@ void FineAudioBuffer::ResetRecord() { } void FineAudioBuffer::GetPlayoutData(int8_t* buffer) { - if (desired_frame_size_bytes_ <= playout_cached_bytes_) { - memcpy(buffer, &playout_cache_buffer_.get()[playout_cached_buffer_start_], - desired_frame_size_bytes_); - playout_cached_buffer_start_ += desired_frame_size_bytes_; - playout_cached_bytes_ -= desired_frame_size_bytes_; - RTC_CHECK_LT(playout_cached_buffer_start_ + playout_cached_bytes_, - bytes_per_10_ms_); - return; - } - memcpy(buffer, &playout_cache_buffer_.get()[playout_cached_buffer_start_], - playout_cached_bytes_); - // Push another n*10ms of audio to |buffer|. n > 1 if - // |desired_frame_size_bytes_| is greater than 10ms of audio. Note that we - // write the audio after the cached bytes copied earlier. - int8_t* unwritten_buffer = &buffer[playout_cached_bytes_]; - int bytes_left = - static_cast(desired_frame_size_bytes_ - playout_cached_bytes_); - // Ceiling of integer division: 1 + ((x - 1) / y) - size_t number_of_requests = 1 + (bytes_left - 1) / (bytes_per_10_ms_); - for (size_t i = 0; i < number_of_requests; ++i) { + const size_t num_bytes = desired_frame_size_bytes_; + // Ask WebRTC for new data in chunks of 10ms until we have enough to + // fulfill the request. It is possible that the buffer already contains + // enough samples from the last round. + while (playout_buffer_.size() < num_bytes) { + // Get 10ms decoded audio from WebRTC. device_buffer_->RequestPlayoutData(samples_per_10_ms_); - int num_out = device_buffer_->GetPlayoutData(unwritten_buffer); - if (static_cast(num_out) != samples_per_10_ms_) { - RTC_CHECK_EQ(num_out, 0); - playout_cached_bytes_ = 0; - return; - } - unwritten_buffer += bytes_per_10_ms_; - RTC_CHECK_GE(bytes_left, 0); - bytes_left -= static_cast(bytes_per_10_ms_); + // Append |bytes_per_10_ms_| elements to the end of the buffer. + const size_t bytes_written = playout_buffer_.AppendData( + bytes_per_10_ms_, [&](rtc::ArrayView buf) { + const size_t samples_per_channel = + device_buffer_->GetPlayoutData(buf.data()); + // TODO(henrika): this class is only used on mobile devices and is + // currently limited to mono. Modifications are needed for stereo. + return sizeof(int16_t) * samples_per_channel; + }); + RTC_DCHECK_EQ(bytes_per_10_ms_, bytes_written); } - RTC_CHECK_LE(bytes_left, 0); - // Put the samples that were written to |buffer| but are not used in the - // cache. - size_t cache_location = desired_frame_size_bytes_; - int8_t* cache_ptr = &buffer[cache_location]; - playout_cached_bytes_ = number_of_requests * bytes_per_10_ms_ - - (desired_frame_size_bytes_ - playout_cached_bytes_); - // If playout_cached_bytes_ is larger than the cache buffer, uninitialized - // memory will be read. - RTC_CHECK_LE(playout_cached_bytes_, bytes_per_10_ms_); - RTC_CHECK_EQ(-bytes_left, playout_cached_bytes_); - playout_cached_buffer_start_ = 0; - memcpy(playout_cache_buffer_.get(), cache_ptr, playout_cached_bytes_); + // Provide the requested number of bytes to the consumer. + memcpy(buffer, playout_buffer_.data(), num_bytes); + // Move remaining samples to start of buffer to prepare for next round. + memmove(playout_buffer_.data(), playout_buffer_.data() + num_bytes, + playout_buffer_.size() - num_bytes); + playout_buffer_.SetSize(playout_buffer_.size() - num_bytes); } void FineAudioBuffer::DeliverRecordedData(const int8_t* buffer, diff --git a/webrtc/modules/audio_device/fine_audio_buffer.h b/webrtc/modules/audio_device/fine_audio_buffer.h index 92f9f41577..306f9d24d3 100644 --- a/webrtc/modules/audio_device/fine_audio_buffer.h +++ b/webrtc/modules/audio_device/fine_audio_buffer.h @@ -42,10 +42,6 @@ class FineAudioBuffer { int sample_rate); ~FineAudioBuffer(); - // Returns the required size of |buffer| when calling GetPlayoutData(). If - // the buffer is smaller memory trampling will happen. - size_t RequiredPlayoutBufferSizeBytes(); - // Clears buffers and counters dealing with playour and/or recording. void ResetPlayout(); void ResetRecord(); @@ -60,8 +56,7 @@ class FineAudioBuffer { // They can be fixed values on most platforms and they are ignored if an // external (hardware/built-in) AEC is used. // The size of |buffer| is given by |size_in_bytes| and must be equal to - // |desired_frame_size_bytes_|. A RTC_CHECK will be hit if this is not the - // case. + // |desired_frame_size_bytes_|. // Example: buffer size is 5ms => call #1 stores 5ms of data, call #2 stores // 5ms of data and sends a total of 10ms to WebRTC and clears the intenal // cache. Call #3 restarts the scheme above. @@ -87,12 +82,7 @@ class FineAudioBuffer { const size_t samples_per_10_ms_; // Number of audio bytes per 10ms. const size_t bytes_per_10_ms_; - // Storage for output samples that are not yet asked for. - std::unique_ptr playout_cache_buffer_; - // Location of first unread output sample. - size_t playout_cached_buffer_start_; - // Number of bytes stored in output (contain samples to be played out) cache. - size_t playout_cached_bytes_; + rtc::BufferT playout_buffer_; // Storage for input samples that are about to be delivered to the WebRTC // ADB or remains from the last successful delivery of a 10ms audio buffer. rtc::BufferT record_buffer_; diff --git a/webrtc/modules/audio_device/fine_audio_buffer_unittest.cc b/webrtc/modules/audio_device/fine_audio_buffer_unittest.cc index 18b203521c..535f16816c 100644 --- a/webrtc/modules/audio_device/fine_audio_buffer_unittest.cc +++ b/webrtc/modules/audio_device/fine_audio_buffer_unittest.cc @@ -118,7 +118,7 @@ void RunFineBufferTest(int sample_rate, int frame_size_in_samples) { sample_rate); std::unique_ptr out_buffer; - out_buffer.reset(new int8_t[fine_buffer.RequiredPlayoutBufferSizeBytes()]); + out_buffer.reset(new int8_t[kFrameSizeBytes]); std::unique_ptr in_buffer; in_buffer.reset(new int8_t[kFrameSizeBytes]); for (int i = 0; i < kNumberOfFrames; ++i) { diff --git a/webrtc/modules/audio_device/ios/audio_device_ios.mm b/webrtc/modules/audio_device/ios/audio_device_ios.mm index bc7ceb8458..5f9e0e7525 100644 --- a/webrtc/modules/audio_device/ios/audio_device_ios.mm +++ b/webrtc/modules/audio_device/ios/audio_device_ios.mm @@ -627,17 +627,11 @@ void AudioDeviceIOS::SetupAudioBuffersForActiveAudioSession() { // or deliver, any number of samples (and not only multiple of 10ms) to match // the native audio unit buffer size. RTC_DCHECK(audio_device_buffer_); + const size_t buffer_size_in_bytes = playout_parameters_.GetBytesPerBuffer(); fine_audio_buffer_.reset(new FineAudioBuffer( - audio_device_buffer_, playout_parameters_.GetBytesPerBuffer(), + audio_device_buffer_, buffer_size_in_bytes, playout_parameters_.sample_rate())); - - // The extra/temporary playoutbuffer must be of this size to avoid - // unnecessary memcpy while caching data between successive callbacks. - const int required_playout_buffer_size = - fine_audio_buffer_->RequiredPlayoutBufferSizeBytes(); - LOG(LS_INFO) << " required playout buffer size: " - << required_playout_buffer_size; - playout_audio_buffer_.reset(new SInt8[required_playout_buffer_size]); + playout_audio_buffer_.reset(new SInt8[buffer_size_in_bytes]); // Allocate AudioBuffers to be used as storage for the received audio. // The AudioBufferList structure works as a placeholder for the