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

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

Issue 100503006: Cleanup OPUS decoder. Remove extraneous transforms and copies. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Android. Created 7 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
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "media/filters/opus_audio_decoder.h" 5 #include "media/filters/opus_audio_decoder.h"
6 6
7 #include <cmath> 7 #include <cmath>
8 8
9 #include "base/bind.h" 9 #include "base/bind.h"
10 #include "base/callback_helpers.h" 10 #include "base/callback_helpers.h"
11 #include "base/location.h" 11 #include "base/location.h"
12 #include "base/message_loop/message_loop_proxy.h" 12 #include "base/message_loop/message_loop_proxy.h"
13 #include "base/sys_byteorder.h" 13 #include "base/sys_byteorder.h"
14 #include "media/base/audio_buffer.h" 14 #include "media/base/audio_buffer.h"
15 #include "media/base/audio_decoder_config.h" 15 #include "media/base/audio_decoder_config.h"
16 #include "media/base/audio_timestamp_helper.h" 16 #include "media/base/audio_timestamp_helper.h"
17 #include "media/base/bind_to_loop.h" 17 #include "media/base/bind_to_loop.h"
18 #include "media/base/buffers.h" 18 #include "media/base/buffers.h"
19 #include "media/base/decoder_buffer.h" 19 #include "media/base/decoder_buffer.h"
20 #include "media/base/demuxer.h" 20 #include "media/base/demuxer.h"
21 #include "media/base/pipeline.h" 21 #include "media/base/pipeline.h"
22 #include "third_party/opus/src/include/opus.h" 22 #include "third_party/opus/src/include/opus.h"
23 #include "third_party/opus/src/include/opus_multistream.h" 23 #include "third_party/opus/src/include/opus_multistream.h"
24 24
25 namespace media { 25 namespace media {
26 26
27 static uint16 ReadLE16(const uint8* data, size_t data_size, int read_offset) { 27 static uint16 ReadLE16(const uint8* data, size_t data_size, int read_offset) {
28 DCHECK(data);
29 uint16 value = 0; 28 uint16 value = 0;
30 DCHECK_LE(read_offset + sizeof(value), data_size); 29 DCHECK_LE(read_offset + sizeof(value), data_size);
31 memcpy(&value, data + read_offset, sizeof(value)); 30 memcpy(&value, data + read_offset, sizeof(value));
32 return base::ByteSwapToLE16(value); 31 return base::ByteSwapToLE16(value);
33 } 32 }
34 33
35 static int TimeDeltaToAudioFrames(base::TimeDelta time_delta, 34 static int TimeDeltaToAudioFrames(base::TimeDelta time_delta,
36 int frame_rate) { 35 int frame_rate) {
37 return std::ceil(time_delta.InSecondsF() * frame_rate); 36 return std::ceil(time_delta.InSecondsF() * frame_rate);
38 } 37 }
39 38
40 // The Opus specification is part of IETF RFC 6716: 39 // The Opus specification is part of IETF RFC 6716:
41 // http://tools.ietf.org/html/rfc6716 40 // http://tools.ietf.org/html/rfc6716
42 41
43 // Opus uses Vorbis channel mapping, and Vorbis channel mapping specifies 42 // Opus uses Vorbis channel mapping, and Vorbis channel mapping specifies
44 // mappings for up to 8 channels. This information is part of the Vorbis I 43 // mappings for up to 8 channels. This information is part of the Vorbis I
45 // Specification: 44 // Specification:
46 // http://www.xiph.org/vorbis/doc/Vorbis_I_spec.html 45 // http://www.xiph.org/vorbis/doc/Vorbis_I_spec.html
47 static const int kMaxVorbisChannels = 8; 46 static const int kMaxVorbisChannels = 8;
48 47
49 // Opus allows for decode of S16 or float samples. OpusAudioDecoder always uses
50 // S16 samples.
51 static const int kBitsPerChannel = 16;
52 static const int kBytesPerChannel = kBitsPerChannel / 8;
53
54 // Maximum packet size used in Xiph's opusdec and FFmpeg's libopusdec. 48 // Maximum packet size used in Xiph's opusdec and FFmpeg's libopusdec.
55 static const int kMaxOpusOutputPacketSizeSamples = 960 * 6 * kMaxVorbisChannels; 49 static const int kMaxOpusOutputPacketSizeSamples = 960 * 6;
56 static const int kMaxOpusOutputPacketSizeBytes =
57 kMaxOpusOutputPacketSizeSamples * kBytesPerChannel;
58 50
59 static void RemapOpusChannelLayout(const uint8* opus_mapping, 51 static void RemapOpusChannelLayout(const uint8* opus_mapping,
60 int num_channels, 52 int num_channels,
61 uint8* channel_layout) { 53 uint8* channel_layout) {
62 DCHECK_LE(num_channels, kMaxVorbisChannels); 54 DCHECK_LE(num_channels, kMaxVorbisChannels);
63 55
64 // Opus uses Vorbis channel layout. 56 // Opus uses Vorbis channel layout.
65 const int32 num_layouts = kMaxVorbisChannels; 57 const int32 num_layouts = kMaxVorbisChannels;
66 const int32 num_layout_values = kMaxVorbisChannels; 58 const int32 num_layout_values = kMaxVorbisChannels;
67 59
(...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after
199 int num_coupled; 191 int num_coupled;
200 uint8 stream_map[kMaxVorbisChannels]; 192 uint8 stream_map[kMaxVorbisChannels];
201 }; 193 };
202 194
203 // Returns true when able to successfully parse and store Opus extra data in 195 // Returns true when able to successfully parse and store Opus extra data in
204 // |extra_data|. Based on opus header parsing code in libopusdec from FFmpeg, 196 // |extra_data|. Based on opus header parsing code in libopusdec from FFmpeg,
205 // and opus_header from Xiph's opus-tools project. 197 // and opus_header from Xiph's opus-tools project.
206 static bool ParseOpusExtraData(const uint8* data, int data_size, 198 static bool ParseOpusExtraData(const uint8* data, int data_size,
207 const AudioDecoderConfig& config, 199 const AudioDecoderConfig& config,
208 OpusExtraData* extra_data) { 200 OpusExtraData* extra_data) {
209 if (data_size < kOpusExtraDataSize) 201 if (data_size < kOpusExtraDataSize) {
202 DLOG(ERROR) << "Extra data size is too small:" << data_size;
210 return false; 203 return false;
204 }
211 205
212 extra_data->channels = *(data + kOpusExtraDataChannelsOffset); 206 extra_data->channels = *(data + kOpusExtraDataChannelsOffset);
213 207
214 if (extra_data->channels <= 0 || extra_data->channels > kMaxVorbisChannels) { 208 if (extra_data->channels <= 0 || extra_data->channels > kMaxVorbisChannels) {
215 DVLOG(0) << "invalid channel count in extra data: " << extra_data->channels; 209 DLOG(ERROR) << "invalid channel count in extra data: "
210 << extra_data->channels;
216 return false; 211 return false;
217 } 212 }
218 213
219 extra_data->skip_samples = 214 extra_data->skip_samples =
220 ReadLE16(data, data_size, kOpusExtraDataSkipSamplesOffset); 215 ReadLE16(data, data_size, kOpusExtraDataSkipSamplesOffset);
221 216
222 extra_data->channel_mapping = *(data + kOpusExtraDataChannelMappingOffset); 217 extra_data->channel_mapping = *(data + kOpusExtraDataChannelMappingOffset);
223 218
224 if (!extra_data->channel_mapping) { 219 if (!extra_data->channel_mapping) {
225 if (extra_data->channels > kMaxChannelsWithDefaultLayout) { 220 if (extra_data->channels > kMaxChannelsWithDefaultLayout) {
226 DVLOG(0) << "Invalid extra data, missing stream map."; 221 DLOG(ERROR) << "Invalid extra data, missing stream map.";
227 return false; 222 return false;
228 } 223 }
229 224
230 extra_data->num_streams = 1; 225 extra_data->num_streams = 1;
231 extra_data->num_coupled = 226 extra_data->num_coupled =
232 (ChannelLayoutToChannelCount(config.channel_layout()) > 1) ? 1 : 0; 227 (ChannelLayoutToChannelCount(config.channel_layout()) > 1) ? 1 : 0;
233 return true; 228 return true;
234 } 229 }
235 230
236 if (data_size < kOpusExtraDataStreamMapOffset + extra_data->channels) { 231 if (data_size < kOpusExtraDataStreamMapOffset + extra_data->channels) {
237 DVLOG(0) << "Invalid stream map; insufficient data for current channel " 232 DLOG(ERROR) << "Invalid stream map; insufficient data for current channel "
238 << "count: " << extra_data->channels; 233 << "count: " << extra_data->channels;
239 return false; 234 return false;
240 } 235 }
241 236
242 extra_data->num_streams = *(data + kOpusExtraDataNumStreamsOffset); 237 extra_data->num_streams = *(data + kOpusExtraDataNumStreamsOffset);
243 extra_data->num_coupled = *(data + kOpusExtraDataNumCoupledOffset); 238 extra_data->num_coupled = *(data + kOpusExtraDataNumCoupledOffset);
244 239
245 if (extra_data->num_streams + extra_data->num_coupled != extra_data->channels) 240 if (extra_data->num_streams + extra_data->num_coupled != extra_data->channels)
246 DVLOG(1) << "Inconsistent channel mapping."; 241 DVLOG(1) << "Inconsistent channel mapping.";
247 242
248 for (int i = 0; i < extra_data->channels; ++i) 243 for (int i = 0; i < extra_data->channels; ++i)
249 extra_data->stream_map[i] = *(data + kOpusExtraDataStreamMapOffset + i); 244 extra_data->stream_map[i] = *(data + kOpusExtraDataStreamMapOffset + i);
250 return true; 245 return true;
251 } 246 }
252 247
253 OpusAudioDecoder::OpusAudioDecoder( 248 OpusAudioDecoder::OpusAudioDecoder(
254 const scoped_refptr<base::MessageLoopProxy>& message_loop) 249 const scoped_refptr<base::MessageLoopProxy>& message_loop)
255 : message_loop_(message_loop), 250 : message_loop_(message_loop),
256 weak_factory_(this), 251 weak_factory_(this),
257 demuxer_stream_(NULL), 252 demuxer_stream_(NULL),
258 opus_decoder_(NULL), 253 opus_decoder_(NULL),
259 bits_per_channel_(0), 254 bits_per_channel_(0),
260 channel_layout_(CHANNEL_LAYOUT_NONE), 255 channel_layout_(CHANNEL_LAYOUT_NONE),
261 samples_per_second_(0), 256 samples_per_second_(0),
257 sample_format_(kUnknownSampleFormat),
262 last_input_timestamp_(kNoTimestamp()), 258 last_input_timestamp_(kNoTimestamp()),
263 frames_to_discard_(0), 259 frames_to_discard_(0),
264 frame_delay_at_start_(0) { 260 frame_delay_at_start_(0) {
265 } 261 }
266 262
267 void OpusAudioDecoder::Initialize( 263 void OpusAudioDecoder::Initialize(
268 DemuxerStream* stream, 264 DemuxerStream* stream,
269 const PipelineStatusCB& status_cb, 265 const PipelineStatusCB& status_cb,
270 const StatisticsCB& statistics_cb) { 266 const StatisticsCB& statistics_cb) {
271 DCHECK(message_loop_->BelongsToCurrentThread()); 267 DCHECK(message_loop_->BelongsToCurrentThread());
272 PipelineStatusCB initialize_cb = BindToCurrentLoop(status_cb); 268 PipelineStatusCB initialize_cb = BindToCurrentLoop(status_cb);
273 269
274 if (demuxer_stream_) { 270 if (demuxer_stream_) {
275 // TODO(scherkus): initialization currently happens more than once in 271 // TODO(scherkus): initialization currently happens more than once in
276 // PipelineIntegrationTest.BasicPlayback. 272 // PipelineIntegrationTest.BasicPlayback.
277 DVLOG(0) << "Initialize has already been called."; 273 DLOG(ERROR) << "Initialize has already been called.";
acolwell GONE FROM CHROMIUM 2013/12/11 23:30:02 This looks like stale code. Please remove.
DaleCurtis 2013/12/12 00:03:24 The whole block? Or just the DLOG?
acolwell GONE FROM CHROMIUM 2013/12/12 00:53:14 The whole block.
278 CHECK(false); 274 CHECK(false);
279 } 275 }
280 276
281 weak_this_ = weak_factory_.GetWeakPtr(); 277 weak_this_ = weak_factory_.GetWeakPtr();
282 demuxer_stream_ = stream; 278 demuxer_stream_ = stream;
283 279
284 if (!ConfigureDecoder()) { 280 if (!ConfigureDecoder()) {
285 initialize_cb.Run(DECODER_ERROR_NOT_SUPPORTED); 281 initialize_cb.Run(DECODER_ERROR_NOT_SUPPORTED);
286 return; 282 return;
287 } 283 }
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after
368 // input buffer is received. 364 // input buffer is received.
369 if (input->end_of_stream()) { 365 if (input->end_of_stream()) {
370 base::ResetAndReturn(&read_cb_).Run(kOk, AudioBuffer::CreateEOSBuffer()); 366 base::ResetAndReturn(&read_cb_).Run(kOk, AudioBuffer::CreateEOSBuffer());
371 return; 367 return;
372 } 368 }
373 369
374 // Make sure we are notified if http://crbug.com/49709 returns. Issue also 370 // Make sure we are notified if http://crbug.com/49709 returns. Issue also
375 // occurs with some damaged files. 371 // occurs with some damaged files.
376 if (input->timestamp() == kNoTimestamp() && 372 if (input->timestamp() == kNoTimestamp() &&
377 output_timestamp_helper_->base_timestamp() == kNoTimestamp()) { 373 output_timestamp_helper_->base_timestamp() == kNoTimestamp()) {
378 DVLOG(1) << "Received a buffer without timestamps!"; 374 DLOG(ERROR) << "Received a buffer without timestamps!";
379 base::ResetAndReturn(&read_cb_).Run(kDecodeError, NULL); 375 base::ResetAndReturn(&read_cb_).Run(kDecodeError, NULL);
380 return; 376 return;
381 } 377 }
382 378
383 if (last_input_timestamp_ != kNoTimestamp() && 379 if (last_input_timestamp_ != kNoTimestamp() &&
384 input->timestamp() != kNoTimestamp() && 380 input->timestamp() != kNoTimestamp() &&
385 input->timestamp() < last_input_timestamp_) { 381 input->timestamp() < last_input_timestamp_) {
386 base::TimeDelta diff = input->timestamp() - last_input_timestamp_; 382 base::TimeDelta diff = input->timestamp() - last_input_timestamp_;
387 DVLOG(1) << "Input timestamps are not monotonically increasing! " 383 DLOG(ERROR) << "Input timestamps are not monotonically increasing! "
388 << " ts " << input->timestamp().InMicroseconds() << " us" 384 << " ts " << input->timestamp().InMicroseconds() << " us"
389 << " diff " << diff.InMicroseconds() << " us"; 385 << " diff " << diff.InMicroseconds() << " us";
390 base::ResetAndReturn(&read_cb_).Run(kDecodeError, NULL); 386 base::ResetAndReturn(&read_cb_).Run(kDecodeError, NULL);
391 return; 387 return;
392 } 388 }
393 389
394 last_input_timestamp_ = input->timestamp(); 390 last_input_timestamp_ = input->timestamp();
395 391
396 scoped_refptr<AudioBuffer> output_buffer; 392 scoped_refptr<AudioBuffer> output_buffer;
397 393
398 if (!Decode(input, &output_buffer)) { 394 if (!Decode(input, &output_buffer)) {
399 base::ResetAndReturn(&read_cb_).Run(kDecodeError, NULL); 395 base::ResetAndReturn(&read_cb_).Run(kDecodeError, NULL);
400 return; 396 return;
401 } 397 }
402 398
403 if (output_buffer.get()) { 399 if (output_buffer.get()) {
404 // Execute callback to return the decoded audio. 400 // Execute callback to return the decoded audio.
405 base::ResetAndReturn(&read_cb_).Run(kOk, output_buffer); 401 base::ResetAndReturn(&read_cb_).Run(kOk, output_buffer);
406 } else { 402 } else {
407 // We exhausted the input data, but it wasn't enough for a frame. Ask for 403 // We exhausted the input data, but it wasn't enough for a frame. Ask for
408 // more data in order to fulfill this read. 404 // more data in order to fulfill this read.
409 ReadFromDemuxerStream(); 405 ReadFromDemuxerStream();
410 } 406 }
411 } 407 }
412 408
413 bool OpusAudioDecoder::ConfigureDecoder() { 409 bool OpusAudioDecoder::ConfigureDecoder() {
414 const AudioDecoderConfig& config = demuxer_stream_->audio_decoder_config(); 410 const AudioDecoderConfig& config = demuxer_stream_->audio_decoder_config();
415 411
416 if (config.codec() != kCodecOpus) { 412 if (config.codec() != kCodecOpus) {
417 DVLOG(0) << "codec must be kCodecOpus."; 413 DLOG(ERROR) << "codec must be kCodecOpus.";
418 return false; 414 return false;
419 } 415 }
420 416
421 const int channel_count = 417 const int channel_count =
422 ChannelLayoutToChannelCount(config.channel_layout()); 418 ChannelLayoutToChannelCount(config.channel_layout());
423 if (!config.IsValidConfig() || channel_count > kMaxVorbisChannels) { 419 if (!config.IsValidConfig() || channel_count > kMaxVorbisChannels) {
424 DVLOG(0) << "Invalid or unsupported audio stream -" 420 DLOG(ERROR) << "Invalid or unsupported audio stream -"
425 << " codec: " << config.codec() 421 << " codec: " << config.codec()
426 << " channel count: " << channel_count 422 << " channel count: " << channel_count
427 << " channel layout: " << config.channel_layout() 423 << " channel layout: " << config.channel_layout()
428 << " bits per channel: " << config.bits_per_channel() 424 << " bits per channel: " << config.bits_per_channel()
429 << " samples per second: " << config.samples_per_second(); 425 << " samples per second: " << config.samples_per_second();
430 return false;
431 }
432
433 if (config.bits_per_channel() != kBitsPerChannel) {
434 DVLOG(0) << "16 bit samples required.";
435 return false; 426 return false;
436 } 427 }
437 428
438 if (config.is_encrypted()) { 429 if (config.is_encrypted()) {
439 DVLOG(0) << "Encrypted audio stream not supported."; 430 DLOG(ERROR) << "Encrypted audio stream not supported.";
440 return false; 431 return false;
441 } 432 }
442 433
443 if (opus_decoder_ && 434 if (opus_decoder_ &&
444 (bits_per_channel_ != config.bits_per_channel() || 435 (bits_per_channel_ != config.bits_per_channel() ||
445 channel_layout_ != config.channel_layout() || 436 channel_layout_ != config.channel_layout() ||
446 samples_per_second_ != config.samples_per_second())) { 437 samples_per_second_ != config.samples_per_second())) {
447 DVLOG(1) << "Unsupported config change :"; 438 DLOG(ERROR) << "Unsupported config change -"
448 DVLOG(1) << "\tbits_per_channel : " << bits_per_channel_ 439 << " bits_per_channel: " << bits_per_channel_
449 << " -> " << config.bits_per_channel(); 440 << " -> " << config.bits_per_channel()
450 DVLOG(1) << "\tchannel_layout : " << channel_layout_ 441 << ", channel_layout: " << channel_layout_
451 << " -> " << config.channel_layout(); 442 << " -> " << config.channel_layout()
452 DVLOG(1) << "\tsample_rate : " << samples_per_second_ 443 << ", sample_rate: " << samples_per_second_
453 << " -> " << config.samples_per_second(); 444 << " -> " << config.samples_per_second();
454 return false; 445 return false;
455 } 446 }
456 447
457 // Clean up existing decoder if necessary. 448 // Clean up existing decoder if necessary.
458 CloseDecoder(); 449 CloseDecoder();
459 450
460 // Allocate the output buffer if necessary.
461 if (!output_buffer_)
462 output_buffer_.reset(new int16[kMaxOpusOutputPacketSizeSamples]);
463
464 // Parse the Opus Extra Data. 451 // Parse the Opus Extra Data.
465 OpusExtraData opus_extra_data; 452 OpusExtraData opus_extra_data;
466 if (!ParseOpusExtraData(config.extra_data(), config.extra_data_size(), 453 if (!ParseOpusExtraData(config.extra_data(), config.extra_data_size(),
467 config, 454 config,
468 &opus_extra_data)) 455 &opus_extra_data))
469 return false; 456 return false;
470 457
471 if (!config.codec_delay().InMicroseconds())
472 return false;
473
474 // Convert from seconds to samples. 458 // Convert from seconds to samples.
475 timestamp_offset_ = config.codec_delay(); 459 timestamp_offset_ = config.codec_delay();
476 frame_delay_at_start_ = TimeDeltaToAudioFrames(config.codec_delay(), 460 frame_delay_at_start_ = TimeDeltaToAudioFrames(config.codec_delay(),
477 config.samples_per_second()); 461 config.samples_per_second());
478 if (frame_delay_at_start_ < 0) { 462 if (timestamp_offset_ <= base::TimeDelta() || frame_delay_at_start_ < 0) {
479 DVLOG(1) << "Invalid file. Incorrect value for codec delay."; 463 DLOG(ERROR) << "Invalid file. Incorrect value for codec delay: "
480 return false; 464 << config.codec_delay().InMicroseconds();
481 }
482 if (frame_delay_at_start_ != opus_extra_data.skip_samples) {
483 DVLOG(1) << "Invalid file. Codec Delay in container does not match the "
484 << "value in Opus Extra Data.";
485 return false; 465 return false;
486 } 466 }
487 467
488 uint8 channel_mapping[kMaxVorbisChannels]; 468 if (frame_delay_at_start_ != opus_extra_data.skip_samples) {
469 DLOG(ERROR) << "Invalid file. Codec Delay in container does not match the "
470 << "value in Opus Extra Data.";
471 return false;
472 }
473
474 uint8 channel_mapping[kMaxVorbisChannels] = {0};
acolwell GONE FROM CHROMIUM 2013/12/11 23:30:02 nit: Why is this change needed? Doesn't it immedia
DaleCurtis 2013/12/12 00:03:24 Not necessary, but good for sanity. The memcpy bel
489 memcpy(&channel_mapping, 475 memcpy(&channel_mapping,
490 kDefaultOpusChannelLayout, 476 kDefaultOpusChannelLayout,
491 kMaxChannelsWithDefaultLayout); 477 kMaxChannelsWithDefaultLayout);
492 478
493 if (channel_count > kMaxChannelsWithDefaultLayout) { 479 if (channel_count > kMaxChannelsWithDefaultLayout) {
494 RemapOpusChannelLayout(opus_extra_data.stream_map, 480 RemapOpusChannelLayout(opus_extra_data.stream_map,
495 channel_count, 481 channel_count,
496 channel_mapping); 482 channel_mapping);
497 } 483 }
498 484
499 // Init Opus. 485 // Init Opus.
500 int status = OPUS_INVALID_STATE; 486 int status = OPUS_INVALID_STATE;
501 opus_decoder_ = opus_multistream_decoder_create(config.samples_per_second(), 487 opus_decoder_ = opus_multistream_decoder_create(config.samples_per_second(),
502 channel_count, 488 channel_count,
503 opus_extra_data.num_streams, 489 opus_extra_data.num_streams,
504 opus_extra_data.num_coupled, 490 opus_extra_data.num_coupled,
505 channel_mapping, 491 channel_mapping,
506 &status); 492 &status);
507 if (!opus_decoder_ || status != OPUS_OK) { 493 if (!opus_decoder_ || status != OPUS_OK) {
508 DVLOG(0) << "opus_multistream_decoder_create failed status=" 494 DLOG(ERROR) << "opus_multistream_decoder_create failed status="
509 << opus_strerror(status); 495 << opus_strerror(status);
510 return false; 496 return false;
511 } 497 }
512 498
513 bits_per_channel_ = config.bits_per_channel(); 499 // Android uses a fixed point build of the opus decoder.
500 #if defined(OS_ANDROID)
acolwell GONE FROM CHROMIUM 2013/12/11 23:30:02 nit: Any reason not to move these changes to the c
DaleCurtis 2013/12/12 00:03:24 Done.
501 sample_format_ = kSampleFormatS16;
502 #else
503 sample_format_ = kSampleFormatF32;
504 #endif
505
506 bits_per_channel_ = SampleFormatToBytesPerChannel(sample_format_) * 8;
514 channel_layout_ = config.channel_layout(); 507 channel_layout_ = config.channel_layout();
515 samples_per_second_ = config.samples_per_second(); 508 samples_per_second_ = config.samples_per_second();
516 output_timestamp_helper_.reset( 509 output_timestamp_helper_.reset(
517 new AudioTimestampHelper(config.samples_per_second())); 510 new AudioTimestampHelper(config.samples_per_second()));
518 return true; 511 return true;
519 } 512 }
520 513
521 void OpusAudioDecoder::CloseDecoder() { 514 void OpusAudioDecoder::CloseDecoder() {
522 if (opus_decoder_) { 515 if (opus_decoder_) {
523 opus_multistream_decoder_destroy(opus_decoder_); 516 opus_multistream_decoder_destroy(opus_decoder_);
524 opus_decoder_ = NULL; 517 opus_decoder_ = NULL;
525 } 518 }
526 } 519 }
527 520
528 void OpusAudioDecoder::ResetTimestampState() { 521 void OpusAudioDecoder::ResetTimestampState() {
529 output_timestamp_helper_->SetBaseTimestamp(kNoTimestamp()); 522 output_timestamp_helper_->SetBaseTimestamp(kNoTimestamp());
530 last_input_timestamp_ = kNoTimestamp(); 523 last_input_timestamp_ = kNoTimestamp();
531 frames_to_discard_ = TimeDeltaToAudioFrames( 524 frames_to_discard_ = TimeDeltaToAudioFrames(
532 demuxer_stream_->audio_decoder_config().seek_preroll(), 525 demuxer_stream_->audio_decoder_config().seek_preroll(),
533 samples_per_second_); 526 samples_per_second_);
534 } 527 }
535 528
536 bool OpusAudioDecoder::Decode(const scoped_refptr<DecoderBuffer>& input, 529 bool OpusAudioDecoder::Decode(const scoped_refptr<DecoderBuffer>& input,
537 scoped_refptr<AudioBuffer>* output_buffer) { 530 scoped_refptr<AudioBuffer>* output_buffer) {
538 int frames_decoded = opus_multistream_decode(opus_decoder_, 531 // Allocate a buffer for the output samples.
539 input->data(), 532 *output_buffer = AudioBuffer::CreateBuffer(
540 input->data_size(), 533 sample_format_,
541 &output_buffer_[0], 534 ChannelLayoutToChannelCount(channel_layout_),
542 kMaxOpusOutputPacketSizeSamples, 535 kMaxOpusOutputPacketSizeSamples);
543 0); 536 const int buffer_size =
537 output_buffer->get()->channel_count() *
538 output_buffer->get()->frame_count() *
539 SampleFormatToBytesPerChannel(sample_format_);
540
541 // Android uses a fixed point build of the opus decoder.
542 #if defined(OS_ANDROID)
acolwell GONE FROM CHROMIUM 2013/12/11 23:30:02 I don't believe we actually even use this code on
DaleCurtis 2013/12/12 00:03:24 Hmm, vignesh said it works on Android? Vignesh?
543 int16* int16_output_buffer = reinterpret_cast<int16*>(
544 output_buffer->get()->channel_data()[0]);
545 int frames_decoded =
546 opus_multistream_decode(opus_decoder_,
547 input->data(),
548 input->data_size(),
549 int16_output_buffer,
550 buffer_size,
551 0);
552 #else
553 float* float_output_buffer = reinterpret_cast<float*>(
554 output_buffer->get()->channel_data()[0]);
555 int frames_decoded =
556 opus_multistream_decode_float(opus_decoder_,
557 input->data(),
558 input->data_size(),
559 float_output_buffer,
560 buffer_size,
561 0);
562 #endif
563
544 if (frames_decoded < 0) { 564 if (frames_decoded < 0) {
545 DVLOG(0) << "opus_multistream_decode failed for" 565 DLOG(ERROR) << "opus_multistream_decode failed for"
546 << " timestamp: " << input->timestamp().InMicroseconds() 566 << " timestamp: " << input->timestamp().InMicroseconds()
547 << " us, duration: " << input->duration().InMicroseconds() 567 << " us, duration: " << input->duration().InMicroseconds()
548 << " us, packet size: " << input->data_size() << " bytes with" 568 << " us, packet size: " << input->data_size() << " bytes with"
549 << " status: " << opus_strerror(frames_decoded); 569 << " status: " << opus_strerror(frames_decoded);
550 return false; 570 return false;
551 } 571 }
552 572
553 uint8* decoded_audio_data = reinterpret_cast<uint8*>(&output_buffer_[0]); 573 // Trim off any extraneous allocation.
554 int bytes_decoded = frames_decoded * 574 DCHECK_LE(frames_decoded, output_buffer->get()->frame_count());
555 demuxer_stream_->audio_decoder_config().bytes_per_frame(); 575 const int trim_frames = output_buffer->get()->frame_count() - frames_decoded;
556 DCHECK_LE(bytes_decoded, kMaxOpusOutputPacketSizeBytes); 576 if (trim_frames > 0)
557 577 output_buffer->get()->TrimEnd(trim_frames);
558 if (output_timestamp_helper_->base_timestamp() == kNoTimestamp() &&
559 !input->end_of_stream()) {
560 DCHECK(input->timestamp() != kNoTimestamp());
561 output_timestamp_helper_->SetBaseTimestamp(input->timestamp());
562 }
563 578
564 // Skip samples should be equal to codec delay when the file starts and when 579 // Skip samples should be equal to codec delay when the file starts and when
565 // there is a seek to zero. 580 // there is a seek to zero.
566 // TODO(vigneshv): This should be checked for start of stream rather than 581 // TODO(vigneshv): This should be checked for start of stream rather than
567 // input timestamp of zero to accomodate streams that don't start at zero. 582 // input timestamp of zero to accomodate streams that don't start at zero.
568 if (input->timestamp() == base::TimeDelta()) 583 if (input->timestamp() == base::TimeDelta())
569 frames_to_discard_ = frame_delay_at_start_; 584 frames_to_discard_ = frame_delay_at_start_;
570 585
571 if (bytes_decoded > 0 && frames_decoded > frames_to_discard_) { 586 // Handle frame discard and trimming.
572 // Copy the audio samples into an output buffer. 587 if (frames_decoded > 0) {
573 uint8* data[] = { decoded_audio_data };
574 *output_buffer = AudioBuffer::CopyFrom(
575 kSampleFormatS16,
576 ChannelLayoutToChannelCount(channel_layout_),
577 frames_decoded,
578 data,
579 output_timestamp_helper_->GetTimestamp() - timestamp_offset_,
580 output_timestamp_helper_->GetFrameDuration(frames_decoded));
581 output_timestamp_helper_->AddFrames(frames_decoded);
582 if (frames_to_discard_ > 0) { 588 if (frames_to_discard_ > 0) {
acolwell GONE FROM CHROMIUM 2013/12/11 23:30:02 This doesn't look right to me. It assumes that fra
DaleCurtis 2013/12/12 00:03:24 Good eye, during testing I realized it was broken.
583 output_buffer->get()->TrimStart(frames_to_discard_); 589 output_buffer->get()->TrimStart(frames_to_discard_);
584 frames_decoded -= frames_to_discard_; 590 frames_decoded -= frames_to_discard_;
585 frames_to_discard_ = 0; 591 frames_to_discard_ = 0;
586 } 592 }
587 if (input->discard_padding().InMicroseconds() > 0) { 593 if (input->discard_padding().InMicroseconds() > 0) {
588 int discard_padding = TimeDeltaToAudioFrames(input->discard_padding(), 594 int discard_padding = TimeDeltaToAudioFrames(input->discard_padding(),
589 samples_per_second_); 595 samples_per_second_);
590 if (discard_padding < 0 || discard_padding > frames_decoded) { 596 if (discard_padding < 0 || discard_padding > frames_decoded) {
591 DVLOG(1) << "Invalid file. Incorrect discard padding value."; 597 DVLOG(1) << "Invalid file. Incorrect discard padding value.";
592 return false; 598 return false;
593 } 599 }
594 output_buffer->get()->TrimEnd(discard_padding); 600 output_buffer->get()->TrimEnd(discard_padding);
595 frames_decoded -= discard_padding; 601 frames_decoded -= discard_padding;
596 } 602 }
597 } else if (bytes_decoded > 0) {
598 frames_to_discard_ -= frames_decoded;
599 frames_decoded = 0;
600 } 603 }
601 604
602 // Decoding finished successfully, update statistics. 605 // Decoding finished successfully, update statistics.
603 PipelineStatistics statistics; 606 PipelineStatistics statistics;
604 statistics.audio_bytes_decoded = input->data_size(); 607 statistics.audio_bytes_decoded = input->data_size();
605 statistics_cb_.Run(statistics); 608 statistics_cb_.Run(statistics);
606 609
607 // Discard the buffer to indicate we need more data. 610 // Discard the buffer to indicate we need more data.
608 if (!frames_decoded) 611 if (!frames_decoded) {
609 *output_buffer = NULL; 612 *output_buffer = NULL;
613 return true;
614 }
615
616 if (output_timestamp_helper_->base_timestamp() == kNoTimestamp() &&
acolwell GONE FROM CHROMIUM 2013/12/11 23:30:02 This also does not look right to me. This assumes
DaleCurtis 2013/12/12 00:03:24 Done. Note: I still have some hackish detection f
617 !input->end_of_stream()) {
618 DCHECK(input->timestamp() != kNoTimestamp());
619 output_timestamp_helper_->SetBaseTimestamp(input->timestamp());
620 }
621
622 output_buffer->get()->set_timestamp(
623 output_timestamp_helper_->GetTimestamp() - timestamp_offset_);
624 output_buffer->get()->set_duration(
625 output_timestamp_helper_->GetFrameDuration(frames_decoded));
626 output_timestamp_helper_->AddFrames(frames_decoded);
610 627
611 return true; 628 return true;
612 } 629 }
613 630
614 } // namespace media 631 } // namespace media
OLDNEW
« media/ffmpeg/ffmpeg_common.cc ('K') | « media/filters/opus_audio_decoder.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698