This source file includes following definitions.
- seen_first_frame_
- InitializationResult
- DecodeFrame
- RecoverBecauseFramesWereDropped
- buffer_
- OpusImpl
- RecoverBecauseFramesWereDropped
- Decode
- Pcm16Impl
- Pcm16Impl
- Decode
- InitializationResult
- DecodeFrame
#include "media/cast/audio_receiver/audio_decoder.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "base/stl_util.h"
#include "base/sys_byteorder.h"
#include "media/cast/cast_defines.h"
#include "third_party/opus/src/include/opus.h"
namespace media {
namespace cast {
class AudioDecoder::ImplBase
: public base::RefCountedThreadSafe<AudioDecoder::ImplBase> {
public:
ImplBase(const scoped_refptr<CastEnvironment>& cast_environment,
transport::AudioCodec codec,
int num_channels,
int sampling_rate)
: cast_environment_(cast_environment),
codec_(codec),
num_channels_(num_channels),
cast_initialization_status_(STATUS_AUDIO_UNINITIALIZED),
seen_first_frame_(false) {
if (num_channels_ <= 0 || sampling_rate <= 0 || sampling_rate % 100 != 0)
cast_initialization_status_ = STATUS_INVALID_AUDIO_CONFIGURATION;
}
CastInitializationStatus InitializationResult() const {
return cast_initialization_status_;
}
void DecodeFrame(scoped_ptr<transport::EncodedAudioFrame> encoded_frame,
const DecodeFrameCallback& callback) {
DCHECK_EQ(cast_initialization_status_, STATUS_AUDIO_INITIALIZED);
scoped_ptr<AudioBus> decoded_audio;
if (encoded_frame->codec != codec_) {
NOTREACHED();
cast_environment_->PostTask(CastEnvironment::MAIN,
FROM_HERE,
base::Bind(callback,
base::Passed(&decoded_audio),
false));
}
COMPILE_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;
decoded_audio = Decode(
reinterpret_cast<uint8*>(string_as_array(&encoded_frame->data)),
static_cast<int>(encoded_frame->data.size()));
cast_environment_->PostTask(CastEnvironment::MAIN,
FROM_HERE,
base::Bind(callback,
base::Passed(&decoded_audio),
is_continuous));
}
protected:
friend class base::RefCountedThreadSafe<ImplBase>;
virtual ~ImplBase() {}
virtual void RecoverBecauseFramesWereDropped() {}
virtual scoped_ptr<AudioBus> Decode(uint8* data, int len) = 0;
const scoped_refptr<CastEnvironment> cast_environment_;
const transport::AudioCodec codec_;
const int num_channels_;
CastInitializationStatus cast_initialization_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<CastEnvironment>& cast_environment,
int num_channels,
int sampling_rate)
: ImplBase(cast_environment,
transport::kOpus,
num_channels,
sampling_rate),
decoder_memory_(new uint8[opus_decoder_get_size(num_channels)]),
opus_decoder_(reinterpret_cast<OpusDecoder*>(decoder_memory_.get())),
max_samples_per_frame_(
kOpusMaxFrameDurationMillis * sampling_rate / 1000),
buffer_(new float[max_samples_per_frame_ * num_channels]) {
if (ImplBase::cast_initialization_status_ != STATUS_AUDIO_UNINITIALIZED)
return;
if (opus_decoder_init(opus_decoder_, sampling_rate, num_channels) !=
OPUS_OK) {
ImplBase::cast_initialization_status_ =
STATUS_INVALID_AUDIO_CONFIGURATION;
return;
}
ImplBase::cast_initialization_status_ = STATUS_AUDIO_INITIALIZED;
}
private:
virtual ~OpusImpl() {}
virtual void RecoverBecauseFramesWereDropped() OVERRIDE {
const opus_int32 result =
opus_decode_float(
opus_decoder_, NULL, 0, buffer_.get(), max_samples_per_frame_, 0);
DCHECK_GE(result, 0);
}
virtual scoped_ptr<AudioBus> Decode(uint8* data, int len) OVERRIDE {
scoped_ptr<AudioBus> 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();
audio_bus = AudioBus::Create(num_channels_, num_samples_decoded).Pass();
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<uint8[]> decoder_memory_;
OpusDecoder* const opus_decoder_;
const int max_samples_per_frame_;
const scoped_ptr<float[]> buffer_;
static const int kOpusMaxFrameDurationMillis = 120;
DISALLOW_COPY_AND_ASSIGN(OpusImpl);
};
class AudioDecoder::Pcm16Impl : public AudioDecoder::ImplBase {
public:
Pcm16Impl(const scoped_refptr<CastEnvironment>& cast_environment,
int num_channels,
int sampling_rate)
: ImplBase(cast_environment,
transport::kPcm16,
num_channels,
sampling_rate) {
if (ImplBase::cast_initialization_status_ != STATUS_AUDIO_UNINITIALIZED)
return;
ImplBase::cast_initialization_status_ = STATUS_AUDIO_INITIALIZED;
}
private:
virtual ~Pcm16Impl() {}
virtual scoped_ptr<AudioBus> Decode(uint8* data, int len) OVERRIDE {
scoped_ptr<AudioBus> 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<int16*>(data);
#if defined(ARCH_CPU_LITTLE_ENDIAN)
const int num_elements = num_samples * num_channels_;
for (int i = 0; i < num_elements; ++i)
pcm_data[i] = static_cast<int16>(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<CastEnvironment>& cast_environment,
const AudioReceiverConfig& audio_config)
: cast_environment_(cast_environment) {
switch (audio_config.codec) {
case transport::kOpus:
impl_ = new OpusImpl(cast_environment,
audio_config.channels,
audio_config.frequency);
break;
case transport::kPcm16:
impl_ = new Pcm16Impl(cast_environment,
audio_config.channels,
audio_config.frequency);
break;
default:
NOTREACHED() << "Unknown or unspecified codec.";
break;
}
}
AudioDecoder::~AudioDecoder() {}
CastInitializationStatus AudioDecoder::InitializationResult() const {
if (impl_)
return impl_->InitializationResult();
return STATUS_UNSUPPORTED_AUDIO_CODEC;
}
void AudioDecoder::DecodeFrame(
scoped_ptr<transport::EncodedAudioFrame> encoded_frame,
const DecodeFrameCallback& callback) {
DCHECK(encoded_frame.get());
DCHECK(!callback.is_null());
if (!impl_ || impl_->InitializationResult() != STATUS_AUDIO_INITIALIZED) {
callback.Run(make_scoped_ptr<AudioBus>(NULL), false);
return;
}
cast_environment_->PostTask(CastEnvironment::AUDIO,
FROM_HERE,
base::Bind(&AudioDecoder::ImplBase::DecodeFrame,
impl_,
base::Passed(&encoded_frame),
callback));
}
}
}