// Copyright 2013 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 "media/cast/audio_sender/audio_encoder.h" #include <algorithm> #include "base/bind.h" #include "base/bind_helpers.h" #include "base/logging.h" #include "base/message_loop/message_loop.h" #include "base/sys_byteorder.h" #include "base/time/time.h" #include "media/base/audio_bus.h" #include "media/cast/cast_defines.h" #include "media/cast/cast_environment.h" #include "third_party/opus/src/include/opus.h" namespace media { namespace cast { void LogAudioEncodedEvent(CastEnvironment* const cast_environment, const base::TimeTicks& recorded_time) { // TODO(mikhal): Resolve timestamp calculation for audio. cast_environment->Logging()->InsertFrameEvent(kAudioFrameEncoded, GetVideoRtpTimestamp(recorded_time), kFrameIdUnknown); } // Base class that handles the common problem of feeding one or more AudioBus' // data into a 10 ms buffer and then, once the buffer is full, encoding the // signal and emitting an EncodedAudioFrame via the FrameEncodedCallback. // // Subclasses complete the implementation by handling the actual encoding // details. class AudioEncoder::ImplBase { public: ImplBase(CastEnvironment* cast_environment, AudioCodec codec, int num_channels, int sampling_rate, const FrameEncodedCallback& callback) : cast_environment_(cast_environment), codec_(codec), num_channels_(num_channels), samples_per_10ms_(sampling_rate / 100), callback_(callback), buffer_fill_end_(0), frame_id_(0) { CHECK_GT(num_channels_, 0); CHECK_GT(samples_per_10ms_, 0); CHECK_EQ(sampling_rate % 100, 0); CHECK_LE(samples_per_10ms_ * num_channels_, EncodedAudioFrame::kMaxNumberOfSamples); } virtual ~ImplBase() {} void EncodeAudio(const AudioBus* audio_bus, const base::TimeTicks& recorded_time, const base::Closure& done_callback) { int src_pos = 0; while (src_pos < audio_bus->frames()) { const int num_samples_to_xfer = std::min(samples_per_10ms_ - buffer_fill_end_, audio_bus->frames() - src_pos); DCHECK_EQ(audio_bus->channels(), num_channels_); TransferSamplesIntoBuffer( audio_bus, src_pos, buffer_fill_end_, num_samples_to_xfer); src_pos += num_samples_to_xfer; buffer_fill_end_ += num_samples_to_xfer; if (src_pos == audio_bus->frames()) { cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE, done_callback); // Note: |audio_bus| is now invalid.. } if (buffer_fill_end_ == samples_per_10ms_) { scoped_ptr<EncodedAudioFrame> audio_frame(new EncodedAudioFrame()); audio_frame->codec = codec_; audio_frame->frame_id = frame_id_++; audio_frame->samples = samples_per_10ms_; if (EncodeFromFilledBuffer(&audio_frame->data)) { // Compute an offset to determine the recorded time for the first // audio sample in the buffer. const base::TimeDelta buffer_time_offset = (buffer_fill_end_ - src_pos) * base::TimeDelta::FromMilliseconds(10) / samples_per_10ms_; // TODO(miu): Consider batching EncodedAudioFrames so we only post a // at most one task for each call to this method. cast_environment_->PostTask( CastEnvironment::MAIN, FROM_HERE, base::Bind(callback_, base::Passed(&audio_frame), recorded_time - buffer_time_offset)); } buffer_fill_end_ = 0; } } } protected: virtual void TransferSamplesIntoBuffer(const AudioBus* audio_bus, int source_offset, int buffer_fill_offset, int num_samples) = 0; virtual bool EncodeFromFilledBuffer(std::string* out) = 0; CastEnvironment* const cast_environment_; const AudioCodec codec_; const int num_channels_; const int samples_per_10ms_; const FrameEncodedCallback callback_; private: // In the case where a call to EncodeAudio() cannot completely fill the // buffer, this points to the position at which to populate data in a later // call. int buffer_fill_end_; // A counter used to label EncodedAudioFrames. uint32 frame_id_; private: DISALLOW_COPY_AND_ASSIGN(ImplBase); }; class AudioEncoder::OpusImpl : public AudioEncoder::ImplBase { public: OpusImpl(CastEnvironment* cast_environment, int num_channels, int sampling_rate, int bitrate, const FrameEncodedCallback& callback) : ImplBase(cast_environment, kOpus, num_channels, sampling_rate, callback), encoder_memory_(new uint8[opus_encoder_get_size(num_channels)]), opus_encoder_(reinterpret_cast<OpusEncoder*>(encoder_memory_.get())), buffer_(new float[num_channels * samples_per_10ms_]) { CHECK_EQ(opus_encoder_init(opus_encoder_, sampling_rate, num_channels, OPUS_APPLICATION_AUDIO), OPUS_OK); if (bitrate <= 0) { // Note: As of 2013-10-31, the encoder in "auto bitrate" mode would use a // variable bitrate up to 102kbps for 2-channel, 48 kHz audio and a 10 ms // frame size. The opus library authors may, of course, adjust this in // later versions. bitrate = OPUS_AUTO; } CHECK_EQ(opus_encoder_ctl(opus_encoder_, OPUS_SET_BITRATE(bitrate)), OPUS_OK); } virtual ~OpusImpl() {} private: virtual void TransferSamplesIntoBuffer(const AudioBus* audio_bus, int source_offset, int buffer_fill_offset, int num_samples) OVERRIDE { // Opus requires channel-interleaved samples in a single array. for (int ch = 0; ch < audio_bus->channels(); ++ch) { const float* src = audio_bus->channel(ch) + source_offset; const float* const src_end = src + num_samples; float* dest = buffer_.get() + buffer_fill_offset * num_channels_ + ch; for (; src < src_end; ++src, dest += num_channels_) *dest = *src; } } virtual bool EncodeFromFilledBuffer(std::string* out) OVERRIDE { out->resize(kOpusMaxPayloadSize); const opus_int32 result = opus_encode_float( opus_encoder_, buffer_.get(), samples_per_10ms_, reinterpret_cast<uint8*>(&out->at(0)), kOpusMaxPayloadSize); if (result > 1) { out->resize(result); return true; } else if (result < 0) { LOG(ERROR) << "Error code from opus_encode_float(): " << result; return false; } else { // Do nothing: The documentation says that a return value of zero or // one byte means the packet does not need to be transmitted. return false; } } const scoped_ptr<uint8[]> encoder_memory_; OpusEncoder* const opus_encoder_; const scoped_ptr<float[]> buffer_; // This is the recommended value, according to documentation in // third_party/opus/src/include/opus.h, so that the Opus encoder does not // degrade the audio due to memory constraints. // // Note: Whereas other RTP implementations do not, the cast library is // perfectly capable of transporting larger than MTU-sized audio frames. static const int kOpusMaxPayloadSize = 4000; DISALLOW_COPY_AND_ASSIGN(OpusImpl); }; class AudioEncoder::Pcm16Impl : public AudioEncoder::ImplBase { public: Pcm16Impl(CastEnvironment* cast_environment, int num_channels, int sampling_rate, const FrameEncodedCallback& callback) : ImplBase(cast_environment, kPcm16, num_channels, sampling_rate, callback), buffer_(new int16[num_channels * samples_per_10ms_]) {} virtual ~Pcm16Impl() {} private: virtual void TransferSamplesIntoBuffer(const AudioBus* audio_bus, int source_offset, int buffer_fill_offset, int num_samples) OVERRIDE { audio_bus->ToInterleavedPartial( source_offset, num_samples, sizeof(int16), buffer_.get() + buffer_fill_offset * num_channels_); } virtual bool EncodeFromFilledBuffer(std::string* out) OVERRIDE { // Output 16-bit PCM integers in big-endian byte order. out->resize(num_channels_ * samples_per_10ms_ * sizeof(int16)); const int16* src = buffer_.get(); const int16* const src_end = src + num_channels_ * samples_per_10ms_; uint16* dest = reinterpret_cast<uint16*>(&out->at(0)); for (; src < src_end; ++src, ++dest) *dest = base::HostToNet16(*src); return true; } private: const scoped_ptr<int16[]> buffer_; DISALLOW_COPY_AND_ASSIGN(Pcm16Impl); }; AudioEncoder::AudioEncoder( const scoped_refptr<CastEnvironment>& cast_environment, const AudioSenderConfig& audio_config, const FrameEncodedCallback& frame_encoded_callback) : cast_environment_(cast_environment) { // Note: It doesn't matter which thread constructs AudioEncoder, just so long // as all calls to InsertAudio() are by the same thread. insert_thread_checker_.DetachFromThread(); switch (audio_config.codec) { case kOpus: impl_.reset(new OpusImpl( cast_environment, audio_config.channels, audio_config.frequency, audio_config.bitrate, frame_encoded_callback)); break; case kPcm16: impl_.reset(new Pcm16Impl( cast_environment, audio_config.channels, audio_config.frequency, frame_encoded_callback)); break; default: NOTREACHED() << "Unsupported or unspecified codec for audio encoder"; break; } } AudioEncoder::~AudioEncoder() {} void AudioEncoder::InsertAudio( const AudioBus* audio_bus, const base::TimeTicks& recorded_time, const base::Closure& done_callback) { DCHECK(insert_thread_checker_.CalledOnValidThread()); if (!impl_) { NOTREACHED(); cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE, done_callback); return; } cast_environment_->PostTask(CastEnvironment::AUDIO_ENCODER, FROM_HERE, base::Bind(&AudioEncoder::EncodeAudio, this, audio_bus, recorded_time, done_callback)); } void AudioEncoder::EncodeAudio( const AudioBus* audio_bus, const base::TimeTicks& recorded_time, const base::Closure& done_callback) { DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::AUDIO_ENCODER)); impl_->EncodeAudio(audio_bus, recorded_time, done_callback); cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE, base::Bind(LogAudioEncodedEvent, cast_environment_, recorded_time)); } } // namespace cast } // namespace media