// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "remoting/codec/audio_encoder_opus.h" #include "base/bind.h" #include "base/logging.h" #include "base/time/time.h" #include "media/base/audio_bus.h" #include "media/base/multi_channel_resampler.h" #include "third_party/opus/src/include/opus.h" namespace remoting { namespace { // Output 160 kb/s bitrate. const int kOutputBitrateBps = 160 * 1024; // Opus doesn't support 44100 sampling rate so we always resample to 48kHz. const AudioPacket::SamplingRate kOpusSamplingRate = AudioPacket::SAMPLING_RATE_48000; // Opus supports frame sizes of 2.5, 5, 10, 20, 40 and 60 ms. We use 20 ms // frames to balance latency and efficiency. const int kFrameSizeMs = 20; // Number of samples per frame when using default sampling rate. const int kFrameSamples = kOpusSamplingRate * kFrameSizeMs / base::Time::kMillisecondsPerSecond; const AudioPacket::BytesPerSample kBytesPerSample = AudioPacket::BYTES_PER_SAMPLE_2; bool IsSupportedSampleRate(int rate) { return rate == 44100 || rate == 48000; } } // namespace AudioEncoderOpus::AudioEncoderOpus() : sampling_rate_(0), channels_(AudioPacket::CHANNELS_STEREO), encoder_(NULL), frame_size_(0), resampling_data_(NULL), resampling_data_size_(0), resampling_data_pos_(0) { } AudioEncoderOpus::~AudioEncoderOpus() { DestroyEncoder(); } void AudioEncoderOpus::InitEncoder() { DCHECK(!encoder_); int error; encoder_ = opus_encoder_create(kOpusSamplingRate, channels_, OPUS_APPLICATION_AUDIO, &error); if (!encoder_) { LOG(ERROR) << "Failed to create OPUS encoder. Error code: " << error; return; } opus_encoder_ctl(encoder_, OPUS_SET_BITRATE(kOutputBitrateBps)); frame_size_ = sampling_rate_ * kFrameSizeMs / base::Time::kMillisecondsPerSecond; if (sampling_rate_ != kOpusSamplingRate) { resample_buffer_.reset( new char[kFrameSamples * kBytesPerSample * channels_]); // TODO(sergeyu): Figure out the right buffer size to use per packet instead // of using media::SincResampler::kDefaultRequestSize. resampler_.reset(new media::MultiChannelResampler( channels_, static_cast<double>(sampling_rate_) / kOpusSamplingRate, media::SincResampler::kDefaultRequestSize, base::Bind(&AudioEncoderOpus::FetchBytesToResample, base::Unretained(this)))); resampler_bus_ = media::AudioBus::Create(channels_, kFrameSamples); } // Drop leftover data because it's for different sampling rate. leftover_samples_ = 0; leftover_buffer_size_ = frame_size_ + media::SincResampler::kDefaultRequestSize; leftover_buffer_.reset( new int16[leftover_buffer_size_ * channels_]); } void AudioEncoderOpus::DestroyEncoder() { if (encoder_) { opus_encoder_destroy(encoder_); encoder_ = NULL; } resampler_.reset(); } bool AudioEncoderOpus::ResetForPacket(AudioPacket* packet) { if (packet->channels() != channels_ || packet->sampling_rate() != sampling_rate_) { DestroyEncoder(); channels_ = packet->channels(); sampling_rate_ = packet->sampling_rate(); if (channels_ <= 0 || channels_ > 2 || !IsSupportedSampleRate(sampling_rate_)) { LOG(WARNING) << "Unsupported OPUS parameters: " << channels_ << " channels with " << sampling_rate_ << " samples per second."; return false; } InitEncoder(); } return encoder_ != NULL; } void AudioEncoderOpus::FetchBytesToResample(int resampler_frame_delay, media::AudioBus* audio_bus) { DCHECK(resampling_data_); int samples_left = (resampling_data_size_ - resampling_data_pos_) / kBytesPerSample / channels_; DCHECK_LE(audio_bus->frames(), samples_left); audio_bus->FromInterleaved( resampling_data_ + resampling_data_pos_, audio_bus->frames(), kBytesPerSample); resampling_data_pos_ += audio_bus->frames() * kBytesPerSample * channels_; DCHECK_LE(resampling_data_pos_, static_cast<int>(resampling_data_size_)); } scoped_ptr<AudioPacket> AudioEncoderOpus::Encode( scoped_ptr<AudioPacket> packet) { DCHECK_EQ(AudioPacket::ENCODING_RAW, packet->encoding()); DCHECK_EQ(1, packet->data_size()); DCHECK_EQ(kBytesPerSample, packet->bytes_per_sample()); if (!ResetForPacket(packet.get())) { LOG(ERROR) << "Encoder initialization failed"; return scoped_ptr<AudioPacket>(); } int samples_in_packet = packet->data(0).size() / kBytesPerSample / channels_; const int16* next_sample = reinterpret_cast<const int16*>(packet->data(0).data()); // Create a new packet of encoded data. scoped_ptr<AudioPacket> encoded_packet(new AudioPacket()); encoded_packet->set_encoding(AudioPacket::ENCODING_OPUS); encoded_packet->set_sampling_rate(kOpusSamplingRate); encoded_packet->set_channels(channels_); int prefetch_samples = resampler_.get() ? media::SincResampler::kDefaultRequestSize : 0; int samples_wanted = frame_size_ + prefetch_samples; while (leftover_samples_ + samples_in_packet >= samples_wanted) { const int16* pcm_buffer = NULL; // Combine the packet with the leftover samples, if any. if (leftover_samples_ > 0) { pcm_buffer = leftover_buffer_.get(); int samples_to_copy = samples_wanted - leftover_samples_; memcpy(leftover_buffer_.get() + leftover_samples_ * channels_, next_sample, samples_to_copy * kBytesPerSample * channels_); } else { pcm_buffer = next_sample; } // Resample data if necessary. int samples_consumed = 0; if (resampler_.get()) { resampling_data_ = reinterpret_cast<const char*>(pcm_buffer); resampling_data_pos_ = 0; resampling_data_size_ = samples_wanted * channels_ * kBytesPerSample; resampler_->Resample(kFrameSamples, resampler_bus_.get()); resampling_data_ = NULL; samples_consumed = resampling_data_pos_ / channels_ / kBytesPerSample; resampler_bus_->ToInterleaved(kFrameSamples, kBytesPerSample, resample_buffer_.get()); pcm_buffer = reinterpret_cast<int16*>(resample_buffer_.get()); } else { samples_consumed = frame_size_; } // Initialize output buffer. std::string* data = encoded_packet->add_data(); data->resize(kFrameSamples * kBytesPerSample * channels_); // Encode. unsigned char* buffer = reinterpret_cast<unsigned char*>(string_as_array(data)); int result = opus_encode(encoder_, pcm_buffer, kFrameSamples, buffer, data->length()); if (result < 0) { LOG(ERROR) << "opus_encode() failed with error code: " << result; return scoped_ptr<AudioPacket>(); } DCHECK_LE(result, static_cast<int>(data->length())); data->resize(result); // Cleanup leftover buffer. if (samples_consumed >= leftover_samples_) { samples_consumed -= leftover_samples_; leftover_samples_ = 0; next_sample += samples_consumed * channels_; samples_in_packet -= samples_consumed; } else { leftover_samples_ -= samples_consumed; memmove(leftover_buffer_.get(), leftover_buffer_.get() + samples_consumed * channels_, leftover_samples_ * channels_ * kBytesPerSample); } } // Store the leftover samples. if (samples_in_packet > 0) { DCHECK_LE(leftover_samples_ + samples_in_packet, leftover_buffer_size_); memmove(leftover_buffer_.get() + leftover_samples_ * channels_, next_sample, samples_in_packet * kBytesPerSample * channels_); leftover_samples_ += samples_in_packet; } // Return NULL if there's nothing in the packet. if (encoded_packet->data_size() == 0) return scoped_ptr<AudioPacket>(); return encoded_packet.Pass(); } } // namespace remoting