Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(260)

Side by Side Diff: chromecast/media/base/cast_audio_decoder_linux.cc

Issue 1494713002: [Chromecast] Move CastAudioDecoder out to chromecast/media/cma/decoder (Closed) Base URL: https://chromium.googlesource.com/chromium/src@master
Patch Set: Created 5 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2015 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.
4
5 #include "chromecast/media/base/cast_audio_decoder.h"
6
7 #include <limits>
8 #include <queue>
9 #include <vector>
10
11 #include "base/bind.h"
12 #include "base/location.h"
13 #include "base/logging.h"
14 #include "base/single_thread_task_runner.h"
15 #include "base/trace_event/trace_event.h"
16 #include "chromecast/media/base/decoder_buffer_base.h"
17 #include "chromecast/media/cma/base/decoder_buffer_adapter.h"
18 #include "chromecast/media/cma/base/decoder_config_adapter.h"
19 #include "media/base/audio_buffer.h"
20 #include "media/base/audio_bus.h"
21 #include "media/base/cdm_context.h"
22 #include "media/base/channel_layout.h"
23 #include "media/base/channel_mixer.h"
24 #include "media/base/decoder_buffer.h"
25 #include "media/base/sample_format.h"
26 #include "media/filters/ffmpeg_audio_decoder.h"
27 #include "media/filters/opus_audio_decoder.h"
28
29 namespace chromecast {
30
31 namespace {
32
33 const int kOpusSamplingRate = 48000;
34 const uint8 kFakeOpusExtraData[19] = {
35 'O', 'p', 'u', 's', 'H', 'e', 'a', 'd', // offset 0, OpusHead
36 0, // offset 8, version
37 2, // offset 9, channels
38 0, 0, // offset 10, skip
39 static_cast<uint8>(kOpusSamplingRate & 0xFF), // offset 12, LE
40 static_cast<uint8>((kOpusSamplingRate >> 8) & 0xFF),
41 static_cast<uint8>((kOpusSamplingRate >> 16) & 0xFF),
42 static_cast<uint8>((kOpusSamplingRate >> 24) & 0xFF),
43 0, 0, // offset 16, gain
44 0, // offset 18, stereo mapping
45 };
46
47 const int kOutputChannelCount = 2; // Always output stereo audio.
48 const int kMaxChannelInput = 2;
49
50 // Copies a chromecast::media::DecoderBufferBase into a ::media::DecoderBuffer.
51 // Currently required because FFmpegAudioDecoder only accepts
52 // ::media::DecoderBuffer.
53 scoped_refptr<::media::DecoderBuffer> ConvertToMediaDecoderBuffer(
54 const scoped_refptr<media::DecoderBufferBase>& input_buffer) {
55 if (input_buffer->end_of_stream())
56 return ::media::DecoderBuffer::CreateEOSBuffer();
57
58 scoped_refptr<::media::DecoderBuffer> copy = ::media::DecoderBuffer::CopyFrom(
59 input_buffer->data(), input_buffer->data_size());
60 copy->set_timestamp(
61 base::TimeDelta::FromMicroseconds(input_buffer->timestamp()));
62 return copy;
63 }
64
65 class CastAudioDecoderImpl : public CastAudioDecoder {
66 public:
67 CastAudioDecoderImpl(
68 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
69 const InitializedCallback& initialized_callback,
70 OutputFormat output_format)
71 : task_runner_(task_runner),
72 initialized_callback_(initialized_callback),
73 output_format_(output_format),
74 initialized_(false),
75 decode_pending_(false),
76 weak_factory_(this) {}
77
78 ~CastAudioDecoderImpl() override {}
79
80 void Initialize(const media::AudioConfig& config) {
81 TRACE_EVENT0("cma", "CastAudioDecoderImpl::Initialize");
82 DCHECK(!initialized_);
83 DCHECK_LE(config_.channel_number, kMaxChannelInput);
84 config_ = config;
85 if (config_.channel_number == 1) {
86 // If the input is mono, create a ChannelMixer to convert mono to stereo.
87 // TODO(kmackay) Support other channel format conversions?
88 mixer_.reset(new ::media::ChannelMixer(::media::CHANNEL_LAYOUT_MONO,
89 ::media::CHANNEL_LAYOUT_STEREO));
90 }
91 base::WeakPtr<CastAudioDecoderImpl> self = weak_factory_.GetWeakPtr();
92 if (config.codec == media::kCodecOpus) {
93 // Insert fake extradata to make OpusAudioDecoder work with v2mirroring.
94 if (config_.extra_data.empty() &&
95 config_.samples_per_second == kOpusSamplingRate &&
96 config_.channel_number == 2)
97 config_.extra_data.assign(
98 kFakeOpusExtraData,
99 kFakeOpusExtraData + sizeof(kFakeOpusExtraData));
100 decoder_.reset(new ::media::OpusAudioDecoder(task_runner_));
101 } else {
102 decoder_.reset(new ::media::FFmpegAudioDecoder(
103 task_runner_, make_scoped_refptr(new ::media::MediaLog())));
104 }
105 decoder_->Initialize(
106 media::DecoderConfigAdapter::ToMediaAudioDecoderConfig(config_),
107 #if !defined(CHROMECAST_BUILD)
108 ::media::SetCdmReadyCB(),
109 #endif
110 base::Bind(&CastAudioDecoderImpl::OnInitialized, self),
111 base::Bind(&CastAudioDecoderImpl::OnDecoderOutput, self));
112 // Unfortunately there is no result from decoder_->Initialize() until later
113 // (the pipeline status callback is posted to the task runner).
114 }
115
116 // CastAudioDecoder implementation:
117 bool Decode(const scoped_refptr<media::DecoderBufferBase>& data,
118 const DecodeCallback& decode_callback) override {
119 TRACE_EVENT0("cma", "CastAudioDecoderImpl::Decode");
120 DCHECK(!decode_callback.is_null());
121 DCHECK(task_runner_->BelongsToCurrentThread());
122 if (!initialized_ || decode_pending_) {
123 decode_queue_.push(std::make_pair(data, decode_callback));
124 } else {
125 DecodeNow(data, decode_callback);
126 }
127 return true;
128 }
129
130 private:
131 typedef std::pair<scoped_refptr<media::DecoderBufferBase>, DecodeCallback>
132 DecodeBufferCallbackPair;
133
134 void DecodeNow(const scoped_refptr<media::DecoderBufferBase>& data,
135 const DecodeCallback& decode_callback) {
136 if (data->end_of_stream()) {
137 // Post the task to ensure that |decode_callback| is not called from
138 // within a call to Decode().
139 task_runner_->PostTask(FROM_HERE,
140 base::Bind(decode_callback, kDecodeOk, data));
141 return;
142 }
143
144 // FFmpegAudioDecoder requires a timestamp to be set, in order to workaround
145 // an FFmpeg bug that was fixed in 2012 :/
146 base::TimeDelta timestamp =
147 base::TimeDelta::FromMicroseconds(data->timestamp());
148 if (timestamp == ::media::kNoTimestamp())
149 data->set_timestamp(base::TimeDelta());
150
151 decode_pending_ = true;
152 // TODO(kmackay) Instead of copying the data to a ::media::DecoderBuffer,
153 // do this more efficiently somehow. Potentially add a method to
154 // FFmpegAudioDecoder to take raw uint8_t* buffers.
155 decoder_->Decode(
156 ConvertToMediaDecoderBuffer(data),
157 base::Bind(&CastAudioDecoderImpl::OnDecodeStatus,
158 weak_factory_.GetWeakPtr(), timestamp, decode_callback));
159 }
160
161 void OnInitialized(bool success) {
162 TRACE_EVENT0("cma", "CastAudioDecoderImpl::OnInitialize");
163 DCHECK(!initialized_);
164 LOG_IF(ERROR, !success) << "Failed to initialize FFmpegAudioDecoder";
165 if (success)
166 initialized_ = true;
167
168 if (success && !decode_queue_.empty()) {
169 const auto& d = decode_queue_.front();
170 DecodeNow(d.first, d.second);
171 decode_queue_.pop();
172 }
173
174 if (!initialized_callback_.is_null())
175 initialized_callback_.Run(initialized_);
176 }
177
178 void OnDecodeStatus(base::TimeDelta buffer_timestamp,
179 const DecodeCallback& decode_callback,
180 ::media::AudioDecoder::Status status) {
181 TRACE_EVENT0("cma", "CastAudioDecoderImpl::OnDecodeStatus");
182 Status result_status = kDecodeOk;
183 scoped_refptr<media::DecoderBufferBase> decoded;
184 if (status == ::media::AudioDecoder::kOk && !decoded_chunks_.empty()) {
185 decoded = ConvertDecoded();
186 } else {
187 if (status != ::media::AudioDecoder::kOk)
188 result_status = kDecodeError;
189 decoded = new media::DecoderBufferAdapter(config_.id,
190 new ::media::DecoderBuffer(0));
191 }
192 decoded_chunks_.clear();
193 decoded->set_timestamp(buffer_timestamp);
194 decode_callback.Run(result_status, decoded);
195
196 // Do not reset decode_pending_ to false until after the callback has
197 // finished running because the callback may call Decode().
198 decode_pending_ = false;
199
200 if (decode_queue_.empty())
201 return;
202
203 const auto& d = decode_queue_.front();
204 // Calling DecodeNow() here does not result in a loop, because
205 // OnDecodeStatus() is always called asynchronously (guaranteed by the
206 // AudioDecoder interface).
207 DecodeNow(d.first, d.second);
208 decode_queue_.pop();
209 }
210
211 void OnDecoderOutput(const scoped_refptr<::media::AudioBuffer>& decoded) {
212 decoded_chunks_.push_back(decoded);
213 }
214
215 scoped_refptr<media::DecoderBufferBase> ConvertDecoded() {
216 DCHECK(!decoded_chunks_.empty());
217 int num_frames = 0;
218 for (auto& chunk : decoded_chunks_)
219 num_frames += chunk->frame_count();
220
221 // Copy decoded data into an AudioBus for conversion.
222 scoped_ptr<::media::AudioBus> decoded =
223 ::media::AudioBus::Create(config_.channel_number, num_frames);
224 int bus_frame_offset = 0;
225 for (auto& chunk : decoded_chunks_) {
226 chunk->ReadFrames(chunk->frame_count(), 0, bus_frame_offset,
227 decoded.get());
228 bus_frame_offset += chunk->frame_count();
229 }
230
231 if (mixer_) {
232 // Convert to stereo if necessary.
233 scoped_ptr<::media::AudioBus> converted_to_stereo =
234 ::media::AudioBus::Create(kOutputChannelCount, num_frames);
235 mixer_->Transform(decoded.get(), converted_to_stereo.get());
236 decoded.swap(converted_to_stereo);
237 }
238
239 // Convert to the desired output format.
240 return FinishConversion(decoded.get());
241 }
242
243 scoped_refptr<media::DecoderBufferBase> FinishConversion(
244 ::media::AudioBus* bus) {
245 DCHECK_EQ(kOutputChannelCount, bus->channels());
246 int size = bus->frames() * kOutputChannelCount *
247 OutputFormatSizeInBytes(output_format_);
248 scoped_refptr<::media::DecoderBuffer> result(
249 new ::media::DecoderBuffer(size));
250
251 if (output_format_ == kOutputSigned16) {
252 bus->ToInterleaved(bus->frames(), OutputFormatSizeInBytes(output_format_),
253 result->writable_data());
254 } else if (output_format_ == kOutputPlanarFloat) {
255 // Data in an AudioBus is already in planar float format; just copy each
256 // channel into the result buffer in order.
257 float* ptr = reinterpret_cast<float*>(result->writable_data());
258 for (int c = 0; c < bus->channels(); ++c) {
259 memcpy(ptr, bus->channel(c), bus->frames() * sizeof(float));
260 ptr += bus->frames();
261 }
262 } else {
263 NOTREACHED();
264 }
265
266 result->set_duration(base::TimeDelta::FromMicroseconds(
267 bus->frames() * base::Time::kMicrosecondsPerSecond /
268 config_.samples_per_second));
269 return make_scoped_refptr(
270 new media::DecoderBufferAdapter(config_.id, result));
271 }
272
273 const scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
274 InitializedCallback initialized_callback_;
275 OutputFormat output_format_;
276 media::AudioConfig config_;
277 scoped_ptr<::media::AudioDecoder> decoder_;
278 std::queue<DecodeBufferCallbackPair> decode_queue_;
279 bool initialized_;
280 scoped_ptr<::media::ChannelMixer> mixer_;
281 bool decode_pending_;
282 std::vector<scoped_refptr<::media::AudioBuffer>> decoded_chunks_;
283 base::WeakPtrFactory<CastAudioDecoderImpl> weak_factory_;
284
285 DISALLOW_COPY_AND_ASSIGN(CastAudioDecoderImpl);
286 };
287
288 } // namespace
289
290 // static
291 scoped_ptr<CastAudioDecoder> CastAudioDecoder::Create(
292 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
293 const media::AudioConfig& config,
294 OutputFormat output_format,
295 const InitializedCallback& initialized_callback) {
296 scoped_ptr<CastAudioDecoderImpl> decoder(new CastAudioDecoderImpl(
297 task_runner, initialized_callback, output_format));
298 decoder->Initialize(config);
299 return decoder.Pass();
300 }
301
302 // static
303 int CastAudioDecoder::OutputFormatSizeInBytes(
304 CastAudioDecoder::OutputFormat format) {
305 switch (format) {
306 case CastAudioDecoder::OutputFormat::kOutputSigned16:
307 return 2;
308 case CastAudioDecoder::OutputFormat::kOutputPlanarFloat:
309 return 4;
310 }
311 NOTREACHED();
312 return 1;
313 }
314
315 } // namespace chromecast
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698