// Copyright 2014 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/receiver/audio_decoder.h" #include "base/bind.h" #include "base/bind_helpers.h" #include "base/location.h" #include "base/logging.h" #include "base/sys_byteorder.h" #include "media/cast/cast_defines.h" #include "third_party/opus/src/include/opus.h" namespace media { namespace cast { // Base class that handles the common problem of detecting dropped frames, and // then invoking the Decode() method implemented by the subclasses to convert // the encoded payload data into usable audio data. class AudioDecoder::ImplBase : public base::RefCountedThreadSafe { public: ImplBase(const scoped_refptr& cast_environment, Codec codec, int num_channels, int sampling_rate) : cast_environment_(cast_environment), codec_(codec), num_channels_(num_channels), operational_status_(STATUS_UNINITIALIZED), seen_first_frame_(false) { if (num_channels_ <= 0 || sampling_rate <= 0 || sampling_rate % 100 != 0) operational_status_ = STATUS_INVALID_CONFIGURATION; } OperationalStatus InitializationResult() const { return operational_status_; } void DecodeFrame(scoped_ptr encoded_frame, const DecodeFrameCallback& callback) { DCHECK_EQ(operational_status_, STATUS_INITIALIZED); static_assert(sizeof(encoded_frame->frame_id) == sizeof(last_frame_id_), "size of frame_id types do not match"); bool is_continuous = true; if (seen_first_frame_) { const uint32 frames_ahead = encoded_frame->frame_id - last_frame_id_; if (frames_ahead > 1) { RecoverBecauseFramesWereDropped(); is_continuous = false; } } else { seen_first_frame_ = true; } last_frame_id_ = encoded_frame->frame_id; scoped_ptr decoded_audio = Decode( encoded_frame->mutable_bytes(), static_cast(encoded_frame->data.size())); scoped_ptr event(new FrameEvent()); event->timestamp = cast_environment_->Clock()->NowTicks(); event->type = FRAME_DECODED; event->media_type = AUDIO_EVENT; event->rtp_timestamp = encoded_frame->rtp_timestamp; event->frame_id = encoded_frame->frame_id; cast_environment_->logger()->DispatchFrameEvent(event.Pass()); cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE, base::Bind(callback, base::Passed(&decoded_audio), is_continuous)); } protected: friend class base::RefCountedThreadSafe; virtual ~ImplBase() {} virtual void RecoverBecauseFramesWereDropped() {} // Note: Implementation of Decode() is allowed to mutate |data|. virtual scoped_ptr Decode(uint8* data, int len) = 0; const scoped_refptr cast_environment_; const Codec codec_; const int num_channels_; // Subclass' ctor is expected to set this to STATUS_INITIALIZED. OperationalStatus operational_status_; private: bool seen_first_frame_; uint32 last_frame_id_; DISALLOW_COPY_AND_ASSIGN(ImplBase); }; class AudioDecoder::OpusImpl : public AudioDecoder::ImplBase { public: OpusImpl(const scoped_refptr& cast_environment, int num_channels, int sampling_rate) : ImplBase(cast_environment, CODEC_AUDIO_OPUS, num_channels, sampling_rate), decoder_memory_(new uint8[opus_decoder_get_size(num_channels)]), opus_decoder_(reinterpret_cast(decoder_memory_.get())), max_samples_per_frame_( kOpusMaxFrameDurationMillis * sampling_rate / 1000), buffer_(new float[max_samples_per_frame_ * num_channels]) { if (ImplBase::operational_status_ != STATUS_UNINITIALIZED) return; if (opus_decoder_init(opus_decoder_, sampling_rate, num_channels) != OPUS_OK) { ImplBase::operational_status_ = STATUS_INVALID_CONFIGURATION; return; } ImplBase::operational_status_ = STATUS_INITIALIZED; } private: ~OpusImpl() final {} void RecoverBecauseFramesWereDropped() final { // Passing NULL for the input data notifies the decoder of frame loss. const opus_int32 result = opus_decode_float( opus_decoder_, NULL, 0, buffer_.get(), max_samples_per_frame_, 0); DCHECK_GE(result, 0); } scoped_ptr Decode(uint8* data, int len) final { scoped_ptr audio_bus; const opus_int32 num_samples_decoded = opus_decode_float( opus_decoder_, data, len, buffer_.get(), max_samples_per_frame_, 0); if (num_samples_decoded <= 0) return audio_bus.Pass(); // Decode error. // Copy interleaved samples from |buffer_| into a new AudioBus (where // samples are stored in planar format, for each channel). audio_bus = AudioBus::Create(num_channels_, num_samples_decoded).Pass(); // TODO(miu): This should be moved into AudioBus::FromInterleaved(). for (int ch = 0; ch < num_channels_; ++ch) { const float* src = buffer_.get() + ch; const float* const src_end = src + num_samples_decoded * num_channels_; float* dest = audio_bus->channel(ch); for (; src < src_end; src += num_channels_, ++dest) *dest = *src; } return audio_bus.Pass(); } const scoped_ptr decoder_memory_; OpusDecoder* const opus_decoder_; const int max_samples_per_frame_; const scoped_ptr buffer_; // According to documentation in third_party/opus/src/include/opus.h, we must // provide enough space in |buffer_| to contain 120ms of samples. At 48 kHz, // then, that means 5760 samples times the number of channels. static const int kOpusMaxFrameDurationMillis = 120; DISALLOW_COPY_AND_ASSIGN(OpusImpl); }; class AudioDecoder::Pcm16Impl : public AudioDecoder::ImplBase { public: Pcm16Impl(const scoped_refptr& cast_environment, int num_channels, int sampling_rate) : ImplBase(cast_environment, CODEC_AUDIO_PCM16, num_channels, sampling_rate) { if (ImplBase::operational_status_ != STATUS_UNINITIALIZED) return; ImplBase::operational_status_ = STATUS_INITIALIZED; } private: ~Pcm16Impl() final {} scoped_ptr Decode(uint8* data, int len) final { scoped_ptr audio_bus; const int num_samples = len / sizeof(int16) / num_channels_; if (num_samples <= 0) return audio_bus.Pass(); int16* const pcm_data = reinterpret_cast(data); #if defined(ARCH_CPU_LITTLE_ENDIAN) // Convert endianness. const int num_elements = num_samples * num_channels_; for (int i = 0; i < num_elements; ++i) pcm_data[i] = static_cast(base::NetToHost16(pcm_data[i])); #endif audio_bus = AudioBus::Create(num_channels_, num_samples).Pass(); audio_bus->FromInterleaved(pcm_data, num_samples, sizeof(int16)); return audio_bus.Pass(); } DISALLOW_COPY_AND_ASSIGN(Pcm16Impl); }; AudioDecoder::AudioDecoder( const scoped_refptr& cast_environment, int channels, int sampling_rate, Codec codec) : cast_environment_(cast_environment) { switch (codec) { case CODEC_AUDIO_OPUS: impl_ = new OpusImpl(cast_environment, channels, sampling_rate); break; case CODEC_AUDIO_PCM16: impl_ = new Pcm16Impl(cast_environment, channels, sampling_rate); break; default: NOTREACHED() << "Unknown or unspecified codec."; break; } } AudioDecoder::~AudioDecoder() {} OperationalStatus AudioDecoder::InitializationResult() const { if (impl_.get()) return impl_->InitializationResult(); return STATUS_UNSUPPORTED_CODEC; } void AudioDecoder::DecodeFrame( scoped_ptr encoded_frame, const DecodeFrameCallback& callback) { DCHECK(encoded_frame.get()); DCHECK(!callback.is_null()); if (!impl_.get() || impl_->InitializationResult() != STATUS_INITIALIZED) { callback.Run(make_scoped_ptr(NULL), false); return; } cast_environment_->PostTask(CastEnvironment::AUDIO, FROM_HERE, base::Bind(&AudioDecoder::ImplBase::DecodeFrame, impl_, base::Passed(&encoded_frame), callback)); } } // namespace cast } // namespace media