diff options
author | henrika@chromium.org <henrika@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-10-11 11:54:31 +0000 |
---|---|---|
committer | henrika@chromium.org <henrika@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-10-11 11:54:31 +0000 |
commit | 7203caf1dc5de35ff9b59f942bdea5f85efbe7c7 (patch) | |
tree | 8fda5c76af6eea034d5f316bd1b4368eeb284e53 | |
parent | 63f3e1e79cdb3816b609ab27a112e3e0beb48243 (diff) | |
download | chromium_src-7203caf1dc5de35ff9b59f942bdea5f85efbe7c7.zip chromium_src-7203caf1dc5de35ff9b59f942bdea5f85efbe7c7.tar.gz chromium_src-7203caf1dc5de35ff9b59f942bdea5f85efbe7c7.tar.bz2 |
Ensures that we always run the low-latency audio capture at natively 128 audio frames. A FIFO is used to adapt to the buffer size requested by the client.
Tested with WebRTC clients in Chrome as well.
Added media_unittests as well for different sample rates.
BUG=154352
TEST=content_unittests --v=1 --gtest_filter=WebRTC*
Review URL: https://chromiumcodereview.appspot.com/11099013
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@161328 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | media/audio/mac/audio_low_latency_input_mac.cc | 73 | ||||
-rw-r--r-- | media/audio/mac/audio_low_latency_input_mac.h | 13 | ||||
-rw-r--r-- | media/audio/mac/audio_low_latency_input_mac_unittest.cc | 13 | ||||
-rw-r--r-- | media/audio/mac/audio_low_latency_output_mac.cc | 33 |
4 files changed, 96 insertions, 36 deletions
diff --git a/media/audio/mac/audio_low_latency_input_mac.cc b/media/audio/mac/audio_low_latency_input_mac.cc index 276f3e7..23e311c 100644 --- a/media/audio/mac/audio_low_latency_input_mac.cc +++ b/media/audio/mac/audio_low_latency_input_mac.cc @@ -11,6 +11,7 @@ #include "base/mac/mac_logging.h" #include "media/audio/audio_util.h" #include "media/audio/mac/audio_manager_mac.h" +#include "media/base/data_buffer.h" namespace media { @@ -60,9 +61,14 @@ AUAudioInputStream::AUAudioInputStream( DVLOG(1) << "Desired ouput format: " << format_; - // Calculate the number of sample frames per callback. - number_of_frames_ = params.GetBytesPerBuffer() / format_.mBytesPerPacket; - DVLOG(1) << "Number of frames per callback: " << number_of_frames_; + // Set number of sample frames per callback used by the internal audio layer. + // An internal FIFO is then utilized to adapt the internal size to the size + // requested by the client. + // Note that we use the same native buffer size as for the output side here + // since the AUHAL implementation requires that both capture and render side + // use the same buffer size. See http://crbug.com/154352 for more details. + number_of_frames_ = GetAudioHardwareBufferSize(); + DVLOG(1) << "Size of data buffer in frames : " << number_of_frames_; // Derive size (in bytes) of the buffers that we will render to. UInt32 data_byte_size = number_of_frames_ * format_.mBytesPerFrame; @@ -78,6 +84,28 @@ AUAudioInputStream::AUAudioInputStream( audio_buffer->mNumberChannels = params.channels(); audio_buffer->mDataByteSize = data_byte_size; audio_buffer->mData = audio_data_buffer_.get(); + + // Set up an internal FIFO buffer that will accumulate recorded audio frames + // until a requested size is ready to be sent to the client. + // It is not possible to ask for less than |kAudioFramesPerCallback| number of + // audio frames. + const size_t requested_size_frames = + params.GetBytesPerBuffer() / format_.mBytesPerPacket; + DCHECK_GE(requested_size_frames, number_of_frames_); + requested_size_bytes_ = requested_size_frames * format_.mBytesPerFrame; + DVLOG(1) << "Requested buffer size in bytes : " << requested_size_bytes_; + DLOG_IF(INFO, requested_size_frames > number_of_frames_) << "FIFO is used"; + + // Allocate some extra memory to avoid memory reallocations. + // Ensure that the size is an even multiple of |number_of_frames_ and + // larger than |requested_size_frames|. + // Example: number_of_frames_=128, requested_size_frames=480 => + // allocated space equals 4*128=512 audio frames + const int max_forward_capacity = format_.mBytesPerFrame * number_of_frames_ * + ((requested_size_frames / number_of_frames_) + 1); + fifo_.reset(new media::SeekableBuffer(0, max_forward_capacity)); + + data_ = new media::DataBuffer(requested_size_bytes_); } AUAudioInputStream::~AUAudioInputStream() {} @@ -465,24 +493,27 @@ OSStatus AUAudioInputStream::Provide(UInt32 number_of_frames, if (!audio_data) return kAudioUnitErr_InvalidElement; - // Unfortunately AUAudioInputStream and AUAudioOutputStream share the frame - // size set by kAudioDevicePropertyBufferFrameSize above on a per process - // basis. What this means is that the |number_of_frames| value may be larger - // or smaller than the value set during Configure(). In this case either - // audio input or audio output will be broken, so just do nothing. - // TODO(henrika): This should never happen so long as we're always using the - // hardware sample rate and the input/output streams configure the same frame - // size. This is currently not true. See http://crbug.com/154352. Once - // fixed, a CHECK() should be added and this wall of text removed. - if (number_of_frames != static_cast<UInt32>(number_of_frames_)) - return noErr; - - // Deliver data packet, delay estimation and volume level to the user. - sink_->OnData(this, - audio_data, - buffer.mDataByteSize, - capture_delay_bytes, - normalized_volume); + // See http://crbug.com/154352 for details. + CHECK_EQ(number_of_frames, static_cast<UInt32>(number_of_frames_)); + + // Accumulate captured audio in FIFO until we can match the output size + // requested by the client. + DCHECK_LE(fifo_->forward_bytes(), requested_size_bytes_); + fifo_->Append(audio_data, buffer.mDataByteSize); + + // Deliver recorded data to the client as soon as the FIFO contains a + // sufficient amount. + if (fifo_->forward_bytes() >= requested_size_bytes_) { + // Read from FIFO into temporary data buffer. + fifo_->Read(data_->GetWritableData(), requested_size_bytes_); + + // Deliver data packet, delay estimation and volume level to the user. + sink_->OnData(this, + data_->GetData(), + requested_size_bytes_, + capture_delay_bytes, + normalized_volume); + } return noErr; } diff --git a/media/audio/mac/audio_low_latency_input_mac.h b/media/audio/mac/audio_low_latency_input_mac.h index 5b1a86d..0c6edc0 100644 --- a/media/audio/mac/audio_low_latency_input_mac.h +++ b/media/audio/mac/audio_low_latency_input_mac.h @@ -45,10 +45,12 @@ #include "media/audio/audio_io.h" #include "media/audio/audio_input_stream_impl.h" #include "media/audio/audio_parameters.h" +#include "media/base/seekable_buffer.h" namespace media { class AudioManagerMac; +class DataBuffer; class AUAudioInputStream : public AudioInputStreamImpl { public: @@ -145,6 +147,17 @@ class AUAudioInputStream : public AudioInputStreamImpl { // when querying the volume of each channel. int number_of_channels_in_frame_; + // Accumulates recorded data packets until the requested size has been stored. + scoped_ptr<media::SeekableBuffer> fifo_; + + // Intermediate storage of data from the FIFO before sending it to the + // client using the OnData() callback. + scoped_refptr<media::DataBuffer> data_; + + // The client requests that the recorded data shall be delivered using + // OnData() callbacks where each callback contains this amount of bytes. + int requested_size_bytes_; + DISALLOW_COPY_AND_ASSIGN(AUAudioInputStream); }; diff --git a/media/audio/mac/audio_low_latency_input_mac_unittest.cc b/media/audio/mac/audio_low_latency_input_mac_unittest.cc index 8b6972c..e8ef33d 100644 --- a/media/audio/mac/audio_low_latency_input_mac_unittest.cc +++ b/media/audio/mac/audio_low_latency_input_mac_unittest.cc @@ -233,8 +233,7 @@ TEST_F(MacAudioInputTest, AUAudioInputStreamVerifyMonoRecording) { // We use 10ms packets and will run the test until ten packets are received. // All should contain valid packets of the same size and a valid delay // estimate. - EXPECT_CALL(sink, OnData(ais, NotNull(), bytes_per_packet, - Ge(bytes_per_packet), _)) + EXPECT_CALL(sink, OnData(ais, NotNull(), bytes_per_packet, _, _)) .Times(AtLeast(10)) .WillRepeatedly(CheckCountAndPostQuitTask(&count, 10, &loop)); ais->Start(&sink); @@ -269,8 +268,14 @@ TEST_F(MacAudioInputTest, AUAudioInputStreamVerifyStereoRecording) { // We use 10ms packets and will run the test until ten packets are received. // All should contain valid packets of the same size and a valid delay // estimate. - EXPECT_CALL(sink, OnData(ais, NotNull(), bytes_per_packet, - Ge(bytes_per_packet), _)) + // TODO(henrika): http://crbug.com/154352 forced us to run the capture side + // using a native buffer size of 128 audio frames and combine it with a FIFO + // to match the requested size by the client. This change might also have + // modified the delay estimates since the existing Ge(bytes_per_packet) for + // parameter #4 does no longer pass. I am removing this restriction here to + // ensure that we can land the patch but will revisit this test again when + // more analysis of the delay estimates are done. + EXPECT_CALL(sink, OnData(ais, NotNull(), bytes_per_packet, _, _)) .Times(AtLeast(10)) .WillRepeatedly(CheckCountAndPostQuitTask(&count, 10, &loop)); ais->Start(&sink); diff --git a/media/audio/mac/audio_low_latency_output_mac.cc b/media/audio/mac/audio_low_latency_output_mac.cc index ea0d04d..8faa67f 100644 --- a/media/audio/mac/audio_low_latency_output_mac.cc +++ b/media/audio/mac/audio_low_latency_output_mac.cc @@ -16,6 +16,19 @@ namespace media { +static std::ostream& operator<<(std::ostream& os, + const AudioStreamBasicDescription& format) { + os << "sample rate : " << format.mSampleRate << std::endl + << "format ID : " << format.mFormatID << std::endl + << "format flags : " << format.mFormatFlags << std::endl + << "bytes per packet : " << format.mBytesPerPacket << std::endl + << "frames per packet : " << format.mFramesPerPacket << std::endl + << "bytes per frame : " << format.mBytesPerFrame << std::endl + << "channels per frame: " << format.mChannelsPerFrame << std::endl + << "bits per channel : " << format.mBitsPerChannel; + return os; +} + // Reorder PCM from AAC layout to Core Audio 5.1 layout. // TODO(fbarchard): Switch layout when ffmpeg is updated. template<class Format> @@ -59,6 +72,7 @@ AUAudioOutputStream::AUAudioOutputStream( audio_bus_(AudioBus::Create(params)) { // We must have a manager. DCHECK(manager_); + // A frame is one sample across all channels. In interleaved audio the per // frame fields identify the set of n |channels|. In uncompressed audio, a // packet is always one frame. @@ -73,8 +87,12 @@ AUAudioOutputStream::AUAudioOutputStream( format_.mBytesPerFrame = format_.mBytesPerPacket; format_.mReserved = 0; + DVLOG(1) << "Desired ouput format: " << format_; + // Calculate the number of sample frames per callback. number_of_frames_ = params.GetBytesPerBuffer() / format_.mBytesPerPacket; + DVLOG(1) << "Number of frames per callback: " << number_of_frames_; + CHECK_EQ(number_of_frames_, GetAudioHardwareBufferSize()); } AUAudioOutputStream::~AUAudioOutputStream() { @@ -157,8 +175,7 @@ bool AUAudioOutputStream::Configure() { // WARNING: Setting this value changes the frame size for all audio units in // the current process. It's imperative that the input and output frame sizes // be the same as audio_util::GetAudioHardwareBufferSize(). - // TODO(henrika): Due to http://crrev.com/159666 this is currently not true - // and should be fixed, a CHECK() should be added at that time. + // See http://crbug.com/154352 for details. UInt32 buffer_size = number_of_frames_; result = AudioUnitSetProperty( output_unit_, @@ -239,15 +256,9 @@ OSStatus AUAudioOutputStream::Render(UInt32 number_of_frames, // size set by kAudioDevicePropertyBufferFrameSize above on a per process // basis. What this means is that the |number_of_frames| value may be larger // or smaller than the value set during Configure(). In this case either - // audio input or audio output will be broken, so just output silence. - // TODO(henrika): This should never happen so long as we're always using the - // hardware sample rate and the input/output streams configure the same frame - // size. This is currently not true. See http://crbug.com/154352. Once - // fixed, a CHECK() should be added and this wall of text removed. - if (number_of_frames != static_cast<UInt32>(audio_bus_->frames())) { - memset(audio_data, 0, number_of_frames * format_.mBytesPerFrame); - return noErr; - } + // audio input or audio output will be broken. + // See http://crbug.com/154352 for details. + CHECK_EQ(number_of_frames, static_cast<UInt32>(audio_bus_->frames())); int frames_filled = source_->OnMoreData( audio_bus_.get(), AudioBuffersState(0, hardware_pending_bytes)); |