Remove implicit conversions from scoped_refptr to T* in media/
[chromium-blink-merge.git] / media / cast / receiver / audio_decoder.cc
blobaeed137d1474cf52f3d2a628f947f04463de35c6
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "media/cast/receiver/audio_decoder.h"
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/location.h"
10 #include "base/logging.h"
11 #include "base/memory/ref_counted.h"
12 #include "base/sys_byteorder.h"
13 #include "media/cast/cast_defines.h"
14 #include "third_party/opus/src/include/opus.h"
16 namespace media {
17 namespace cast {
19 // Base class that handles the common problem of detecting dropped frames, and
20 // then invoking the Decode() method implemented by the subclasses to convert
21 // the encoded payload data into usable audio data.
22 class AudioDecoder::ImplBase
23 : public base::RefCountedThreadSafe<AudioDecoder::ImplBase> {
24 public:
25 ImplBase(const scoped_refptr<CastEnvironment>& cast_environment,
26 Codec codec,
27 int num_channels,
28 int sampling_rate)
29 : cast_environment_(cast_environment),
30 codec_(codec),
31 num_channels_(num_channels),
32 cast_initialization_status_(STATUS_AUDIO_UNINITIALIZED),
33 seen_first_frame_(false) {
34 if (num_channels_ <= 0 || sampling_rate <= 0 || sampling_rate % 100 != 0)
35 cast_initialization_status_ = STATUS_INVALID_AUDIO_CONFIGURATION;
38 CastInitializationStatus InitializationResult() const {
39 return cast_initialization_status_;
42 void DecodeFrame(scoped_ptr<EncodedFrame> encoded_frame,
43 const DecodeFrameCallback& callback) {
44 DCHECK_EQ(cast_initialization_status_, STATUS_AUDIO_INITIALIZED);
46 COMPILE_ASSERT(sizeof(encoded_frame->frame_id) == sizeof(last_frame_id_),
47 size_of_frame_id_types_do_not_match);
48 bool is_continuous = true;
49 if (seen_first_frame_) {
50 const uint32 frames_ahead = encoded_frame->frame_id - last_frame_id_;
51 if (frames_ahead > 1) {
52 RecoverBecauseFramesWereDropped();
53 is_continuous = false;
55 } else {
56 seen_first_frame_ = true;
58 last_frame_id_ = encoded_frame->frame_id;
60 scoped_ptr<AudioBus> decoded_audio = Decode(
61 encoded_frame->mutable_bytes(),
62 static_cast<int>(encoded_frame->data.size()));
63 cast_environment_->PostTask(CastEnvironment::MAIN,
64 FROM_HERE,
65 base::Bind(callback,
66 base::Passed(&decoded_audio),
67 is_continuous));
70 protected:
71 friend class base::RefCountedThreadSafe<ImplBase>;
72 virtual ~ImplBase() {}
74 virtual void RecoverBecauseFramesWereDropped() {}
76 // Note: Implementation of Decode() is allowed to mutate |data|.
77 virtual scoped_ptr<AudioBus> Decode(uint8* data, int len) = 0;
79 const scoped_refptr<CastEnvironment> cast_environment_;
80 const Codec codec_;
81 const int num_channels_;
83 // Subclass' ctor is expected to set this to STATUS_AUDIO_INITIALIZED.
84 CastInitializationStatus cast_initialization_status_;
86 private:
87 bool seen_first_frame_;
88 uint32 last_frame_id_;
90 DISALLOW_COPY_AND_ASSIGN(ImplBase);
93 class AudioDecoder::OpusImpl : public AudioDecoder::ImplBase {
94 public:
95 OpusImpl(const scoped_refptr<CastEnvironment>& cast_environment,
96 int num_channels,
97 int sampling_rate)
98 : ImplBase(cast_environment,
99 CODEC_AUDIO_OPUS,
100 num_channels,
101 sampling_rate),
102 decoder_memory_(new uint8[opus_decoder_get_size(num_channels)]),
103 opus_decoder_(reinterpret_cast<OpusDecoder*>(decoder_memory_.get())),
104 max_samples_per_frame_(
105 kOpusMaxFrameDurationMillis * sampling_rate / 1000),
106 buffer_(new float[max_samples_per_frame_ * num_channels]) {
107 if (ImplBase::cast_initialization_status_ != STATUS_AUDIO_UNINITIALIZED)
108 return;
109 if (opus_decoder_init(opus_decoder_, sampling_rate, num_channels) !=
110 OPUS_OK) {
111 ImplBase::cast_initialization_status_ =
112 STATUS_INVALID_AUDIO_CONFIGURATION;
113 return;
115 ImplBase::cast_initialization_status_ = STATUS_AUDIO_INITIALIZED;
118 private:
119 virtual ~OpusImpl() {}
121 virtual void RecoverBecauseFramesWereDropped() OVERRIDE {
122 // Passing NULL for the input data notifies the decoder of frame loss.
123 const opus_int32 result =
124 opus_decode_float(
125 opus_decoder_, NULL, 0, buffer_.get(), max_samples_per_frame_, 0);
126 DCHECK_GE(result, 0);
129 virtual scoped_ptr<AudioBus> Decode(uint8* data, int len) OVERRIDE {
130 scoped_ptr<AudioBus> audio_bus;
131 const opus_int32 num_samples_decoded = opus_decode_float(
132 opus_decoder_, data, len, buffer_.get(), max_samples_per_frame_, 0);
133 if (num_samples_decoded <= 0)
134 return audio_bus.Pass(); // Decode error.
136 // Copy interleaved samples from |buffer_| into a new AudioBus (where
137 // samples are stored in planar format, for each channel).
138 audio_bus = AudioBus::Create(num_channels_, num_samples_decoded).Pass();
139 // TODO(miu): This should be moved into AudioBus::FromInterleaved().
140 for (int ch = 0; ch < num_channels_; ++ch) {
141 const float* src = buffer_.get() + ch;
142 const float* const src_end = src + num_samples_decoded * num_channels_;
143 float* dest = audio_bus->channel(ch);
144 for (; src < src_end; src += num_channels_, ++dest)
145 *dest = *src;
147 return audio_bus.Pass();
150 const scoped_ptr<uint8[]> decoder_memory_;
151 OpusDecoder* const opus_decoder_;
152 const int max_samples_per_frame_;
153 const scoped_ptr<float[]> buffer_;
155 // According to documentation in third_party/opus/src/include/opus.h, we must
156 // provide enough space in |buffer_| to contain 120ms of samples. At 48 kHz,
157 // then, that means 5760 samples times the number of channels.
158 static const int kOpusMaxFrameDurationMillis = 120;
160 DISALLOW_COPY_AND_ASSIGN(OpusImpl);
163 class AudioDecoder::Pcm16Impl : public AudioDecoder::ImplBase {
164 public:
165 Pcm16Impl(const scoped_refptr<CastEnvironment>& cast_environment,
166 int num_channels,
167 int sampling_rate)
168 : ImplBase(cast_environment,
169 CODEC_AUDIO_PCM16,
170 num_channels,
171 sampling_rate) {
172 if (ImplBase::cast_initialization_status_ != STATUS_AUDIO_UNINITIALIZED)
173 return;
174 ImplBase::cast_initialization_status_ = STATUS_AUDIO_INITIALIZED;
177 private:
178 virtual ~Pcm16Impl() {}
180 virtual scoped_ptr<AudioBus> Decode(uint8* data, int len) OVERRIDE {
181 scoped_ptr<AudioBus> audio_bus;
182 const int num_samples = len / sizeof(int16) / num_channels_;
183 if (num_samples <= 0)
184 return audio_bus.Pass();
186 int16* const pcm_data = reinterpret_cast<int16*>(data);
187 #if defined(ARCH_CPU_LITTLE_ENDIAN)
188 // Convert endianness.
189 const int num_elements = num_samples * num_channels_;
190 for (int i = 0; i < num_elements; ++i)
191 pcm_data[i] = static_cast<int16>(base::NetToHost16(pcm_data[i]));
192 #endif
193 audio_bus = AudioBus::Create(num_channels_, num_samples).Pass();
194 audio_bus->FromInterleaved(pcm_data, num_samples, sizeof(int16));
195 return audio_bus.Pass();
198 DISALLOW_COPY_AND_ASSIGN(Pcm16Impl);
201 AudioDecoder::AudioDecoder(
202 const scoped_refptr<CastEnvironment>& cast_environment,
203 int channels,
204 int sampling_rate,
205 Codec codec)
206 : cast_environment_(cast_environment) {
207 switch (codec) {
208 case CODEC_AUDIO_OPUS:
209 impl_ = new OpusImpl(cast_environment, channels, sampling_rate);
210 break;
211 case CODEC_AUDIO_PCM16:
212 impl_ = new Pcm16Impl(cast_environment, channels, sampling_rate);
213 break;
214 default:
215 NOTREACHED() << "Unknown or unspecified codec.";
216 break;
220 AudioDecoder::~AudioDecoder() {}
222 CastInitializationStatus AudioDecoder::InitializationResult() const {
223 if (impl_.get())
224 return impl_->InitializationResult();
225 return STATUS_UNSUPPORTED_AUDIO_CODEC;
228 void AudioDecoder::DecodeFrame(
229 scoped_ptr<EncodedFrame> encoded_frame,
230 const DecodeFrameCallback& callback) {
231 DCHECK(encoded_frame.get());
232 DCHECK(!callback.is_null());
233 if (!impl_.get() ||
234 impl_->InitializationResult() != STATUS_AUDIO_INITIALIZED) {
235 callback.Run(make_scoped_ptr<AudioBus>(NULL), false);
236 return;
238 cast_environment_->PostTask(CastEnvironment::AUDIO,
239 FROM_HERE,
240 base::Bind(&AudioDecoder::ImplBase::DecodeFrame,
241 impl_,
242 base::Passed(&encoded_frame),
243 callback));
246 } // namespace cast
247 } // namespace media