webrtc_m130/webrtc/modules/audio_device/fine_audio_buffer.cc
henrika 86d907cffd Refactor the AudioDevice for iOS and improve the performance and stability
This CL contains major modifications of the audio output parts for WebRTC on iOS:
- general code cleanup
- improves thread handling (added thread checks, remove critical section, atomic ops etc.)
- reduces loopback latency of iPhone 6 from ~90ms to ~60ms ;-)
- improves selection of audio parameters on iOS
- reduces complexity by removing complex and redundant delay estimates
- now instead uses fixed delay estimates if for some reason the SW EAC must be used
- adds AudioFineBuffer to compensate for differences in native output buffer size and
  the 10ms size used by WebRTC. Same class as is used today on Android and we have unit tests for
  this class (the old code was buggy and we have several issue reports of crashes related to it)

Similar improvements will be done for the recording sid as well in a separate CL.
I will also add support for 48kHz in an upcoming CL since that will improve Opus performance.

BUG=webrtc:4796,webrtc:4817,webrtc:4954, webrtc:4212
TEST=AppRTC demo and iOS modules_unittests using --gtest_filter=AudioDevice*
R=pbos@webrtc.org, tkchin@webrtc.org

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

Cr-Commit-Position: refs/heads/master@{#9875}
2015-09-07 14:10:10 +00:00

151 lines
6.3 KiB
C++

/*
* Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "webrtc/modules/audio_device/fine_audio_buffer.h"
#include <memory.h>
#include <stdio.h>
#include <algorithm>
#include "webrtc/base/checks.h"
#include "webrtc/base/logging.h"
#include "webrtc/modules/audio_device/audio_device_buffer.h"
namespace webrtc {
FineAudioBuffer::FineAudioBuffer(AudioDeviceBuffer* device_buffer,
size_t desired_frame_size_bytes,
int sample_rate)
: device_buffer_(device_buffer),
desired_frame_size_bytes_(desired_frame_size_bytes),
sample_rate_(sample_rate),
samples_per_10_ms_(static_cast<size_t>(sample_rate_ * 10 / 1000)),
bytes_per_10_ms_(samples_per_10_ms_ * sizeof(int16_t)),
playout_cached_buffer_start_(0),
playout_cached_bytes_(0),
// Allocate extra space on the recording side to reduce the number of
// memmove() calls.
required_record_buffer_size_bytes_(
5 * (desired_frame_size_bytes + bytes_per_10_ms_)),
record_cached_bytes_(0),
record_read_pos_(0),
record_write_pos_(0) {
playout_cache_buffer_.reset(new int8_t[bytes_per_10_ms_]);
record_cache_buffer_.reset(new int8_t[required_record_buffer_size_bytes_]);
memset(record_cache_buffer_.get(), 0, required_record_buffer_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_);
}
void FineAudioBuffer::ResetRecord() {
record_cached_bytes_ = 0;
record_read_pos_ = 0;
record_write_pos_ = 0;
memset(record_cache_buffer_.get(), 0, required_record_buffer_size_bytes_);
}
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_;
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<int>(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) {
device_buffer_->RequestPlayoutData(samples_per_10_ms_);
int num_out = device_buffer_->GetPlayoutData(unwritten_buffer);
if (static_cast<size_t>(num_out) != samples_per_10_ms_) {
CHECK_EQ(num_out, 0);
playout_cached_bytes_ = 0;
return;
}
unwritten_buffer += bytes_per_10_ms_;
CHECK_GE(bytes_left, 0);
bytes_left -= static_cast<int>(bytes_per_10_ms_);
}
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.
CHECK_LE(playout_cached_bytes_, bytes_per_10_ms_);
CHECK_EQ(static_cast<size_t>(-bytes_left), playout_cached_bytes_);
playout_cached_buffer_start_ = 0;
memcpy(playout_cache_buffer_.get(), cache_ptr, playout_cached_bytes_);
}
void FineAudioBuffer::DeliverRecordedData(const int8_t* buffer,
size_t size_in_bytes,
int playout_delay_ms,
int record_delay_ms) {
CHECK_EQ(size_in_bytes, desired_frame_size_bytes_);
// Check if the temporary buffer can store the incoming buffer. If not,
// move the remaining (old) bytes to the beginning of the temporary buffer
// and start adding new samples after the old samples.
if (record_write_pos_ + size_in_bytes > required_record_buffer_size_bytes_) {
if (record_cached_bytes_ > 0) {
memmove(record_cache_buffer_.get(),
record_cache_buffer_.get() + record_read_pos_,
record_cached_bytes_);
}
record_write_pos_ = record_cached_bytes_;
record_read_pos_ = 0;
}
// Add recorded samples to a temporary buffer.
memcpy(record_cache_buffer_.get() + record_write_pos_, buffer, size_in_bytes);
record_write_pos_ += size_in_bytes;
record_cached_bytes_ += size_in_bytes;
// Consume samples in temporary buffer in chunks of 10ms until there is not
// enough data left. The number of remaining bytes in the cache is given by
// |record_cached_bytes_| after this while loop is done.
while (record_cached_bytes_ >= bytes_per_10_ms_) {
device_buffer_->SetRecordedBuffer(
record_cache_buffer_.get() + record_read_pos_, samples_per_10_ms_);
device_buffer_->SetVQEData(playout_delay_ms, record_delay_ms, 0);
device_buffer_->DeliverRecordedData();
// Read next chunk of 10ms data.
record_read_pos_ += bytes_per_10_ms_;
// Reduce number of cached bytes with the consumed amount.
record_cached_bytes_ -= bytes_per_10_ms_;
}
}
} // namespace webrtc