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

Side by Side Diff: media/filters/opus_audio_decoder.cc

Issue 11416367: Add Opus decode wrapper to media. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Revert mime_util changes. Created 8 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 | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2012 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 "media/filters/opus_audio_decoder.h"
6
7 #include "base/bind.h"
8 #include "base/callback_helpers.h"
9 #include "base/location.h"
10 #include "base/message_loop_proxy.h"
11 #include "base/sys_byteorder.h"
12 #include "media/base/audio_decoder_config.h"
13 #include "media/base/audio_timestamp_helper.h"
14 #include "media/base/data_buffer.h"
15 #include "media/base/decoder_buffer.h"
16 #include "media/base/demuxer.h"
17 #include "media/base/pipeline.h"
18 #include "third_party/opus/src/include/opus.h"
19 #include "third_party/opus/src/include/opus_multistream.h"
20
21 namespace media {
22
23 static uint16 ReadLE16(const uint8* data, size_t data_size, int read_offset) {
24 DCHECK(data);
25 uint16 value = 0;
26 DCHECK_LE(read_offset + sizeof(value), data_size);
27 memcpy(&value, data + read_offset, sizeof(value));
28 return base::ByteSwapToLE16(value);
29 }
30
31 // Returns true if the decode result was end of stream.
32 static inline bool IsEndOfStream(int decoded_size, Buffer* input) {
scherkus (not reviewing) 2012/12/14 23:39:28 this function doesn't appear to be used
Tom Finegan 2012/12/14 23:58:39 It's used in one place (at the end of Decode()).
33 // Two conditions to meet to declare end of stream for this decoder:
34 // 1. Opus didn't output anything.
35 // 2. An end of stream buffer is received.
36 return decoded_size == 0 && input->IsEndOfStream();
37 }
38
39 // The Opus specification is part of IETF RFC 6716:
40 // http://tools.ietf.org/html/rfc6716
41
42 // Opus uses Vorbis channel mapping, and Vorbis channel mapping specifies
43 // mappings for up to 8 channels. See section 4.3.9 of the vorbis
44 // specification:
45 // http://www.xiph.org/vorbis/doc/Vorbis_I_spec.html
46 static const int kMaxVorbisChannels = 8;
47
48 // Opus allows for decode of S16 or float samples. OpusAudioDecoder always uses
49 // S16 samples.
50 static const int kBitsPerChannel = 16;
51 static const int kBytesPerChannel = kBitsPerChannel / 8;
52
53 // Maximum packet size used in Xiph's opusdec and FFmpeg's libopusdec.
54 static const int kMaxOpusOutputPacketSizeSamples = 960 * 6;
55 static const int kMaxOpusOutputPacketSizeBytes =
56 kMaxOpusOutputPacketSizeSamples * kBytesPerChannel;
57
58 static void RemapOpusChannelLayout(const uint8* opus_mapping,
59 int num_channels,
60 uint8* channel_layout) {
61 DCHECK(opus_mapping);
scherkus (not reviewing) 2012/12/14 23:39:28 nit: these check-for-null DCHECK()s aren't very he
Tom Finegan 2012/12/14 23:58:39 Done.
62 DCHECK(channel_layout);
63 DCHECK_LE(num_channels, kMaxVorbisChannels);
64
65 // Opus uses Vorbis channel layout.
66 const int32 num_layouts = kMaxVorbisChannels;
67 const int32 num_layout_values = kMaxVorbisChannels;
68 const uint8 kVorbisChannelLayouts[num_layouts][num_layout_values] = {
69 { 0 },
70 { 0, 1 },
71 { 0, 2, 1 },
72 { 0, 1, 2, 3 },
73 { 0, 2, 1, 3, 4 },
74 { 0, 2, 1, 5, 3, 4 },
75 { 0, 2, 1, 6, 5, 3, 4 },
76 { 0, 2, 1, 7, 5, 6, 3, 4 },
77 };
78
79 // Reorder the channels to produce the same ordering as FFmpeg, which is
80 // what the pipeline expects.
81 const uint8* vorbis_layout_offset = kVorbisChannelLayouts[num_channels - 1];
82 for (int channel = 0; channel < num_channels; ++channel)
83 channel_layout[channel] = opus_mapping[vorbis_layout_offset[channel]];
84 }
85
86 // Opus Header contents:
87 // - "OpusHead" (64 bits)
88 // - version number (8 bits)
89 // - Channels C (8 bits)
90 // - Pre-skip (16 bits)
91 // - Sampling rate (32 bits)
92 // - Gain in dB (16 bits, S7.8)
93 // - Mapping (8 bits, 0=single stream (mono/stereo) 1=Vorbis mapping,
94 // 2..254: reserved, 255: multistream with no mapping)
95 //
96 // - if (mapping != 0)
97 // - N = totel number of streams (8 bits)
98 // - M = number of paired streams (8 bits)
99 // - C times channel origin
100 // - if (C<2*M)
101 // - stream = byte/2
102 // - if (byte&0x1 == 0)
103 // - left
104 // else
105 // - right
106 // - else
107 // - stream = byte-M
108
109 // Default audio output channel layout. Used to initialize |stream_map| in
110 // OpusHeader, and passed to opus_multistream_decoder_create() when the header
111 // does not contain mapping information. The values are valid only for mono and
112 // stereo output: Opus streams with more than 2 channels require a stream map.
113 static const int kMaxChannelsWithDefaultLayout = 2;
114 static const uint8 kDefaultOpusChannelLayout[kMaxChannelsWithDefaultLayout] = {
115 0, 1 };
116
117 // Size of the Opus header excluding optional mapping information.
118 static const int kOpusHeaderSize = 19;
119
120 // Offset to the channel count byte in the Opus header.
121 static const int kOpusHeaderChannelsOffset = 9;
122
123 // Offset to the pre-skip value in the Opus header.
124 static const int kOpusHeaderSkipSamplesOffset = 10;
125
126 // Offset to the channel mapping byte in the Opus header.
127 static const int kOpusHeaderChannelMappingOffset = 18;
128
129 // Header contains a stream map. The mapping values are in extra data beyond
130 // the always present |kOpusHeaderSize| bytes of data. The mapping data
131 // contains stream count, coupling information, and per channel mapping values:
132 // - Byte 0: Number of streams.
133 // - Byte 1: Number coupled.
134 // - Byte 2: Starting at byte 2 are |header->channels| uint8 mapping values.
135 static const int kOpusHeaderNumStreamsOffset = kOpusHeaderSize;
136 static const int kOpusHeaderNumCoupledOffset = kOpusHeaderNumStreamsOffset + 1;
137 static const int kOpusHeaderStreamMapOffset = kOpusHeaderNumStreamsOffset + 2;
138
139 struct OpusHeader {
140 OpusHeader()
141 : channels(0),
142 skip_samples(0),
143 channel_mapping(0),
144 num_streams(0),
145 num_coupled(0) {
146 memcpy(stream_map,
147 kDefaultOpusChannelLayout,
148 kMaxChannelsWithDefaultLayout);
149 }
150 int channels;
151 int skip_samples;
152 int channel_mapping;
153 int num_streams;
154 int num_coupled;
155 uint8 stream_map[kMaxVorbisChannels];
156 };
157
158 // Returns true when able to successfully parse and store Opus header data in
159 // data parsed in |header|. Based on opus header parsing code in libopusdec
160 // from FFmpeg, and opus_header from Xiph's opus-tools project.
161 static void ParseOpusHeader(const uint8* data, int data_size,
162 const AudioDecoderConfig& config,
163 OpusHeader* header) {
164 DCHECK(data);
scherkus (not reviewing) 2012/12/14 23:39:28 nit: these check-for-null DCHECK()s aren't very he
Tom Finegan 2012/12/14 23:58:39 Done.
165 DCHECK(header);
166 DCHECK_GE(data_size, kOpusHeaderSize);
167
168 header->channels = *(data + kOpusHeaderChannelsOffset);
169
170 DCHECK(header->channels > 0 && header->channels <= kMaxVorbisChannels)
scherkus (not reviewing) 2012/12/14 23:39:28 if this is file parsing code you may opt for CHECK
Tom Finegan 2012/12/14 23:58:39 Used CHECK per offline discussion (this is file pa
171 << "invalid channel count in header: " << header->channels;
172
173 header->skip_samples =
174 ReadLE16(data, data_size, kOpusHeaderSkipSamplesOffset);
175
176 header->channel_mapping = *(data + kOpusHeaderChannelMappingOffset);
177
178 if (!header->channel_mapping) {
179 DCHECK_LE(header->channels, kMaxChannelsWithDefaultLayout)
180 << "Invalid header, missing stream map.";
181
182 header->num_streams = 1;
183 header->num_coupled =
184 (ChannelLayoutToChannelCount(config.channel_layout()) > 1) ? 1 : 0;
185 return;
186 }
187
188 const int mapping_required_size =
scherkus (not reviewing) 2012/12/14 23:39:28 nit: I don't see this used -- perhaps roll the add
189 kOpusHeaderStreamMapOffset + header->channels;
190 DCHECK_GE(data_size, mapping_required_size) << "Invalid stream map.";
191
192 header->num_streams = *(data + kOpusHeaderNumStreamsOffset);
193 header->num_coupled = *(data + kOpusHeaderNumCoupledOffset);
194
195 if (header->num_streams + header->num_coupled != header->channels)
196 LOG(WARNING) << "Inconsistent channel mapping.";
197
198 for (int i = 0; i < kMaxVorbisChannels; ++i)
199 header->stream_map[i] = *(data + kOpusHeaderStreamMapOffset + i);
200 }
201
202 OpusAudioDecoder::OpusAudioDecoder(
203 const scoped_refptr<base::MessageLoopProxy>& message_loop)
204 : message_loop_(message_loop),
205 opus_decoder_(NULL),
206 bits_per_channel_(0),
207 channel_layout_(CHANNEL_LAYOUT_NONE),
208 samples_per_second_(0),
209 last_input_timestamp_(kNoTimestamp()),
210 output_bytes_to_drop_(0),
211 skip_samples_(0) {
212 }
213
214 void OpusAudioDecoder::Initialize(
215 const scoped_refptr<DemuxerStream>& stream,
216 const PipelineStatusCB& status_cb,
217 const StatisticsCB& statistics_cb) {
218 if (!message_loop_->BelongsToCurrentThread()) {
219 message_loop_->PostTask(FROM_HERE, base::Bind(
220 &OpusAudioDecoder::DoInitialize, this,
221 stream, status_cb, statistics_cb));
222 return;
223 }
224 DoInitialize(stream, status_cb, statistics_cb);
225 }
226
227 void OpusAudioDecoder::Read(const ReadCB& read_cb) {
228 // Complete operation asynchronously on different stack of execution as per
229 // the API contract of AudioDecoder::Read()
230 message_loop_->PostTask(FROM_HERE, base::Bind(
231 &OpusAudioDecoder::DoRead, this, read_cb));
232 }
233
234 int OpusAudioDecoder::bits_per_channel() {
235 return bits_per_channel_;
236 }
237
238 ChannelLayout OpusAudioDecoder::channel_layout() {
239 return channel_layout_;
240 }
241
242 int OpusAudioDecoder::samples_per_second() {
243 return samples_per_second_;
244 }
245
246 void OpusAudioDecoder::Reset(const base::Closure& closure) {
247 message_loop_->PostTask(FROM_HERE, base::Bind(
248 &OpusAudioDecoder::DoReset, this, closure));
249 }
250
251 OpusAudioDecoder::~OpusAudioDecoder() {
252 // TODO(scherkus): should we require Stop() to be called? this might end up
253 // getting called on a random thread due to refcounting.
254 CloseDecoder();
255 }
256
257 void OpusAudioDecoder::DoInitialize(
258 const scoped_refptr<DemuxerStream>& stream,
259 const PipelineStatusCB& status_cb,
260 const StatisticsCB& statistics_cb) {
261 if (demuxer_stream_) {
262 // TODO(scherkus): initialization currently happens more than once in
263 // PipelineIntegrationTest.BasicPlayback.
264 LOG(ERROR) << "Initialize has already been called.";
265 CHECK(false);
266 }
267
268 demuxer_stream_ = stream;
269
270 if (!ConfigureDecoder()) {
271 status_cb.Run(DECODER_ERROR_NOT_SUPPORTED);
272 return;
273 }
274
275 statistics_cb_ = statistics_cb;
276 status_cb.Run(PIPELINE_OK);
277 }
278
279 void OpusAudioDecoder::DoReset(const base::Closure& closure) {
280 opus_multistream_decoder_ctl(opus_decoder_, OPUS_RESET_STATE);
281 ResetTimestampState();
282 closure.Run();
283 }
284
285 void OpusAudioDecoder::DoRead(const ReadCB& read_cb) {
286 DCHECK(message_loop_->BelongsToCurrentThread());
287 DCHECK(!read_cb.is_null());
288 CHECK(read_cb_.is_null()) << "Overlapping decodes are not supported.";
289
290 read_cb_ = read_cb;
291 ReadFromDemuxerStream();
292 }
293
294 void OpusAudioDecoder::DoDecodeBuffer(
295 DemuxerStream::Status status,
296 const scoped_refptr<DecoderBuffer>& input) {
297 if (!message_loop_->BelongsToCurrentThread()) {
298 message_loop_->PostTask(FROM_HERE, base::Bind(
299 &OpusAudioDecoder::DoDecodeBuffer, this, status, input));
300 return;
301 }
302
303 DCHECK(!read_cb_.is_null());
304 DCHECK_EQ(status != DemuxerStream::kOk, !input) << status;
305
306 if (status == DemuxerStream::kAborted) {
307 DCHECK(!input);
308 base::ResetAndReturn(&read_cb_).Run(kAborted, NULL);
309 return;
310 }
311
312 if (status == DemuxerStream::kConfigChanged) {
313 DCHECK(!input);
314
315 scoped_refptr<DataBuffer> output_buffer;
316
317 // Send a "end of stream" buffer to the decode loop
318 // to output any remaining data still in the decoder.
319 if (!Decode(DecoderBuffer::CreateEOSBuffer(), true, &output_buffer)) {
320 base::ResetAndReturn(&read_cb_).Run(kDecodeError, NULL);
321 return;
322 }
323
324 DVLOG(1) << "Config changed.";
325
326 if (!ConfigureDecoder()) {
327 base::ResetAndReturn(&read_cb_).Run(kDecodeError, NULL);
328 return;
329 }
330
331 ResetTimestampState();
332
333 if (output_buffer) {
334 // Execute callback to return the decoded audio.
335 base::ResetAndReturn(&read_cb_).Run(kOk, output_buffer);
336 } else {
337 // We exhausted the input data, but it wasn't enough for a frame. Ask for
338 // more data in order to fulfill this read.
339 ReadFromDemuxerStream();
340 }
341 }
342
343 DCHECK_EQ(status, DemuxerStream::kOk);
344 DCHECK(input);
345
346 // Make sure we are notified if http://crbug.com/49709 returns. Issue also
347 // occurs with some damaged files.
348 if (!input->IsEndOfStream() && input->GetTimestamp() == kNoTimestamp() &&
349 output_timestamp_helper_->base_timestamp() == kNoTimestamp()) {
350 DVLOG(1) << "Received a buffer without timestamps!";
351 base::ResetAndReturn(&read_cb_).Run(kDecodeError, NULL);
352 return;
353 }
354
355 if (!input->IsEndOfStream()) {
356 if (last_input_timestamp_ != kNoTimestamp() &&
357 input->GetTimestamp() != kNoTimestamp() &&
358 input->GetTimestamp() < last_input_timestamp_) {
359 base::TimeDelta diff = input->GetTimestamp() - last_input_timestamp_;
360 DVLOG(1) << "Input timestamps are not monotonically increasing! "
361 << " ts " << input->GetTimestamp().InMicroseconds() << " us"
362 << " diff " << diff.InMicroseconds() << " us";
363 base::ResetAndReturn(&read_cb_).Run(kDecodeError, NULL);
364 }
365
366 last_input_timestamp_ = input->GetTimestamp();
367 }
368
369 scoped_refptr<DataBuffer> output_buffer;
370
371 if (!Decode(input, false, &output_buffer)) {
372 base::ResetAndReturn(&read_cb_).Run(kDecodeError, NULL);
373 return;
374 }
375
376 if (output_buffer) {
377 // Execute callback to return the decoded audio.
378 base::ResetAndReturn(&read_cb_).Run(kOk, output_buffer);
379 } else {
380 // We exhausted the input data, but it wasn't enough for a frame. Ask for
381 // more data in order to fulfill this read.
382 ReadFromDemuxerStream();
383 }
384 }
385
386 void OpusAudioDecoder::ReadFromDemuxerStream() {
387 DCHECK(!read_cb_.is_null());
388
389 demuxer_stream_->Read(base::Bind(&OpusAudioDecoder::DoDecodeBuffer, this));
390 }
391
392 bool OpusAudioDecoder::ConfigureDecoder() {
393 const AudioDecoderConfig& config = demuxer_stream_->audio_decoder_config();
394
395 if (config.codec() != kCodecOpus) {
396 DLOG(ERROR) << "ConfigureDecoder(): codec must be kCodecOpus.";
397 return false;
398 }
399
400 const int channel_count =
401 ChannelLayoutToChannelCount(config.channel_layout());
402 if (!config.IsValidConfig() || channel_count > kMaxVorbisChannels) {
403 DLOG(ERROR) << "ConfigureDecoder(): Invalid or unsupported audio stream -"
404 << " codec: " << config.codec()
405 << " channel count: " << channel_count
406 << " channel layout: " << config.channel_layout()
407 << " bits per channel: " << config.bits_per_channel()
408 << " samples per second: " << config.samples_per_second();
409 return false;
410 }
411
412 if (config.bits_per_channel() != kBitsPerChannel) {
413 DLOG(ERROR) << "ConfigureDecoder(): 16 bit samples required.";
414 return false;
415 }
416
417 if (config.is_encrypted()) {
418 DLOG(ERROR) << "ConfigureDecoder(): Encrypted audio stream not supported.";
419 return false;
420 }
421
422 if (opus_decoder_ &&
423 (bits_per_channel_ != config.bits_per_channel() ||
424 channel_layout_ != config.channel_layout() ||
425 samples_per_second_ != config.samples_per_second())) {
426 DVLOG(1) << "Unsupported config change :";
427 DVLOG(1) << "\tbits_per_channel : " << bits_per_channel_
428 << " -> " << config.bits_per_channel();
429 DVLOG(1) << "\tchannel_layout : " << channel_layout_
430 << " -> " << config.channel_layout();
431 DVLOG(1) << "\tsample_rate : " << samples_per_second_
432 << " -> " << config.samples_per_second();
433 return false;
434 }
435
436 // Clean up existing decoder if necessary.
437 CloseDecoder();
438
439 // Allocate the output buffer if necessary.
440 if (!output_buffer_)
441 output_buffer_.reset(new int16[kMaxOpusOutputPacketSizeSamples]);
442
443 // Parse the Opus header.
444 OpusHeader opus_header;
445 ParseOpusHeader(config.extra_data(), config.extra_data_size(),
446 config,
447 &opus_header);
448
449 skip_samples_ = opus_header.skip_samples;
450
451 if (skip_samples_ > 0)
452 output_bytes_to_drop_ = skip_samples_ * config.bytes_per_frame();
453
454 uint8 channel_mapping[kMaxVorbisChannels];
455 memcpy(&channel_mapping,
456 kDefaultOpusChannelLayout,
457 kMaxChannelsWithDefaultLayout);
458
459 if (channel_count > kMaxChannelsWithDefaultLayout) {
460 RemapOpusChannelLayout(opus_header.stream_map,
461 channel_count,
462 channel_mapping);
463 }
464
465 // Init Opus.
466 int status = OPUS_INVALID_STATE;
467 opus_decoder_ = opus_multistream_decoder_create(config.samples_per_second(),
468 channel_count,
469 opus_header.num_streams,
470 opus_header.num_coupled,
471 channel_mapping,
472 &status);
473 if (!opus_decoder_ || status != OPUS_OK) {
474 LOG(ERROR) << "ConfigureDecoder(): opus_multistream_decoder_create failed"
475 << " status=" << opus_strerror(status);
476 return false;
477 }
478
479 // TODO(tomfinegan): Handle audio delay once the matroska spec is updated
480 // to represent the value.
481
482 bits_per_channel_ = config.bits_per_channel();
483 channel_layout_ = config.channel_layout();
484 samples_per_second_ = config.samples_per_second();
485 output_timestamp_helper_.reset(new AudioTimestampHelper(
486 config.bytes_per_frame(), config.samples_per_second()));
487 return true;
488 }
489
490 void OpusAudioDecoder::CloseDecoder() {
491 if (opus_decoder_) {
492 opus_multistream_decoder_destroy(opus_decoder_);
493 opus_decoder_ = NULL;
494 }
495 }
496
497 void OpusAudioDecoder::ResetTimestampState() {
498 output_timestamp_helper_->SetBaseTimestamp(kNoTimestamp());
499 last_input_timestamp_ = kNoTimestamp();
500 output_bytes_to_drop_ = 0;
501 }
502
503 bool OpusAudioDecoder::Decode(const scoped_refptr<DecoderBuffer>& input,
504 bool skip_eos_append,
505 scoped_refptr<DataBuffer>* output_buffer) {
506 int samples_decoded =
507 opus_multistream_decode(opus_decoder_,
508 input->GetData(), input->GetDataSize(),
509 &output_buffer_[0],
510 kMaxOpusOutputPacketSizeSamples,
511 0);
512 if (samples_decoded < 0) {
513 DCHECK(!input->IsEndOfStream())
514 << "Decode(): End of stream buffer produced an error!";
515
516 LOG(ERROR) << "ConfigureDecoder(): opus_multistream_decode failed for"
517 << " timestamp: " << input->GetTimestamp().InMicroseconds()
518 << " us, duration: " << input->GetDuration().InMicroseconds()
519 << " us, packet size: " << input->GetDataSize() << " bytes with"
520 << " status: " << opus_strerror(samples_decoded);
521 return false;
522 }
523
524 uint8* decoded_audio_data = reinterpret_cast<uint8*>(&output_buffer_[0]);
525 int decoded_audio_size = samples_decoded *
526 demuxer_stream_->audio_decoder_config().bytes_per_frame();
527 DCHECK_LE(decoded_audio_size, kMaxOpusOutputPacketSizeBytes);
528
529 if (output_timestamp_helper_->base_timestamp() == kNoTimestamp() &&
530 !input->IsEndOfStream()) {
531 DCHECK(input->GetTimestamp() != kNoTimestamp());
532 output_timestamp_helper_->SetBaseTimestamp(input->GetTimestamp());
533 }
534
535 if (decoded_audio_size > 0 && output_bytes_to_drop_ > 0) {
536 int dropped_size = std::min(decoded_audio_size, output_bytes_to_drop_);
537 DCHECK_EQ(dropped_size % kBytesPerChannel, 0);
538 decoded_audio_data += dropped_size;
539 decoded_audio_size -= dropped_size;
540 output_bytes_to_drop_ -= dropped_size;
541 }
542
543 if (decoded_audio_size > 0) {
544 // Copy the audio samples into an output buffer.
545 *output_buffer = new DataBuffer(decoded_audio_data, decoded_audio_size);
546 (*output_buffer)->SetTimestamp(output_timestamp_helper_->GetTimestamp());
547 (*output_buffer)->SetDuration(
548 output_timestamp_helper_->GetDuration(decoded_audio_size));
549 output_timestamp_helper_->AddBytes(decoded_audio_size);
550 } else if (IsEndOfStream(decoded_audio_size, input) && !skip_eos_append) {
551 DCHECK_EQ(input->GetDataSize(), 0);
552 // Create an end of stream output buffer.
553 *output_buffer = new DataBuffer(0);
554 }
555
556 // Decoding finished successfully, update statistics.
557 PipelineStatistics statistics;
558 statistics.audio_bytes_decoded = decoded_audio_size;
559 statistics_cb_.Run(statistics);
560
561 return true;
562 }
563
564 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698