Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 Loading... | |
| 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 Loading... | |
| 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 |
| OLD | NEW |