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

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

Powered by Google App Engine
This is Rietveld 408576698