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/single_thread_task_runner.h" | 9 #include "base/single_thread_task_runner.h" |
| 10 #include "base/sys_byteorder.h" | 10 #include "base/sys_byteorder.h" |
| 11 #include "media/base/audio_buffer.h" | 11 #include "media/base/audio_buffer.h" |
| 12 #include "media/base/audio_decoder_config.h" | 12 #include "media/base/audio_decoder_config.h" |
| 13 #include "media/base/audio_discard_helper.h" | 13 #include "media/base/audio_discard_helper.h" |
| 14 #include "media/base/bind_to_current_loop.h" | 14 #include "media/base/bind_to_current_loop.h" |
| 15 #include "media/base/buffers.h" | 15 #include "media/base/buffers.h" |
| 16 #include "media/base/decoder_buffer.h" | 16 #include "media/base/decoder_buffer.h" |
| 17 #include "third_party/opus/src/include/opus.h" | 17 #include "third_party/opus/src/include/opus.h" |
| 18 #include "third_party/opus/src/include/opus_multistream.h" | 18 #include "third_party/opus/src/include/opus_multistream.h" |
| 19 | 19 |
| 20 namespace media { | 20 namespace media { |
| 21 | 21 |
| 22 static base::TimeDelta FramesToTimeDelta(int frames, double sample_rate) { | |
| 23 return base::TimeDelta::FromMicroseconds( | |
| 24 frames * base::Time::kMicrosecondsPerSecond / sample_rate); | |
| 25 } | |
| 26 | |
| 22 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) { |
| 23 uint16 value = 0; | 28 uint16 value = 0; |
| 24 DCHECK_LE(read_offset + sizeof(value), data_size); | 29 DCHECK_LE(read_offset + sizeof(value), data_size); |
| 25 memcpy(&value, data + read_offset, sizeof(value)); | 30 memcpy(&value, data + read_offset, sizeof(value)); |
| 26 return base::ByteSwapToLE16(value); | 31 return base::ByteSwapToLE16(value); |
| 27 } | 32 } |
| 28 | 33 |
| 29 // The Opus specification is part of IETF RFC 6716: | 34 // The Opus specification is part of IETF RFC 6716: |
| 30 // http://tools.ietf.org/html/rfc6716 | 35 // http://tools.ietf.org/html/rfc6716 |
| 31 | 36 |
| (...skipping 206 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 238 if (extra_data->num_streams + extra_data->num_coupled != extra_data->channels) | 243 if (extra_data->num_streams + extra_data->num_coupled != extra_data->channels) |
| 239 DVLOG(1) << "Inconsistent channel mapping."; | 244 DVLOG(1) << "Inconsistent channel mapping."; |
| 240 | 245 |
| 241 for (int i = 0; i < extra_data->channels; ++i) | 246 for (int i = 0; i < extra_data->channels; ++i) |
| 242 extra_data->stream_map[i] = *(data + kOpusExtraDataStreamMapOffset + i); | 247 extra_data->stream_map[i] = *(data + kOpusExtraDataStreamMapOffset + i); |
| 243 return true; | 248 return true; |
| 244 } | 249 } |
| 245 | 250 |
| 246 OpusAudioDecoder::OpusAudioDecoder( | 251 OpusAudioDecoder::OpusAudioDecoder( |
| 247 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) | 252 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) |
| 248 : task_runner_(task_runner), | 253 : task_runner_(task_runner), opus_decoder_(nullptr) {} |
| 249 opus_decoder_(NULL), | |
| 250 start_input_timestamp_(kNoTimestamp()) {} | |
| 251 | 254 |
| 252 std::string OpusAudioDecoder::GetDisplayName() const { | 255 std::string OpusAudioDecoder::GetDisplayName() const { |
| 253 return "OpusAudioDecoder"; | 256 return "OpusAudioDecoder"; |
| 254 } | 257 } |
| 255 | 258 |
| 256 void OpusAudioDecoder::Initialize(const AudioDecoderConfig& config, | 259 void OpusAudioDecoder::Initialize(const AudioDecoderConfig& config, |
| 257 const InitCB& init_cb, | 260 const InitCB& init_cb, |
| 258 const OutputCB& output_cb) { | 261 const OutputCB& output_cb) { |
| 259 DCHECK(task_runner_->BelongsToCurrentThread()); | 262 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 260 InitCB bound_init_cb = BindToCurrentLoop(init_cb); | 263 InitCB bound_init_cb = BindToCurrentLoop(init_cb); |
| (...skipping 25 matching lines...) Expand all Loading... | |
| 286 task_runner_->PostTask(FROM_HERE, closure); | 289 task_runner_->PostTask(FROM_HERE, closure); |
| 287 } | 290 } |
| 288 | 291 |
| 289 OpusAudioDecoder::~OpusAudioDecoder() { | 292 OpusAudioDecoder::~OpusAudioDecoder() { |
| 290 DCHECK(task_runner_->BelongsToCurrentThread()); | 293 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 291 | 294 |
| 292 if (!opus_decoder_) | 295 if (!opus_decoder_) |
| 293 return; | 296 return; |
| 294 | 297 |
| 295 opus_multistream_decoder_ctl(opus_decoder_, OPUS_RESET_STATE); | 298 opus_multistream_decoder_ctl(opus_decoder_, OPUS_RESET_STATE); |
| 296 ResetTimestampState(); | |
| 297 CloseDecoder(); | 299 CloseDecoder(); |
| 298 } | 300 } |
| 299 | 301 |
| 300 void OpusAudioDecoder::DecodeBuffer( | 302 void OpusAudioDecoder::DecodeBuffer( |
| 301 const scoped_refptr<DecoderBuffer>& input, | 303 const scoped_refptr<DecoderBuffer>& input, |
| 302 const DecodeCB& decode_cb) { | 304 const DecodeCB& decode_cb) { |
| 303 DCHECK(task_runner_->BelongsToCurrentThread()); | 305 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 304 DCHECK(!decode_cb.is_null()); | 306 DCHECK(!decode_cb.is_null()); |
| 305 DCHECK(input.get()); | 307 DCHECK(input.get()); |
| 306 | 308 |
| 307 // Libopus does not buffer output. Decoding is complete when an end of stream | 309 // Libopus does not buffer output. Decoding is complete when an end of stream |
| 308 // input buffer is received. | 310 // input buffer is received. |
| 309 if (input->end_of_stream()) { | 311 if (input->end_of_stream()) { |
| 310 decode_cb.Run(kOk); | 312 decode_cb.Run(kOk); |
| 311 return; | 313 return; |
| 312 } | 314 } |
| 313 | 315 |
| 314 // Make sure we are notified if http://crbug.com/49709 returns. Issue also | 316 // Make sure we are notified if http://crbug.com/49709 returns. Issue also |
| 315 // occurs with some damaged files. | 317 // occurs with some damaged files. |
| 316 if (input->timestamp() == kNoTimestamp()) { | 318 if (input->timestamp() == kNoTimestamp()) { |
| 317 DLOG(ERROR) << "Received a buffer without timestamps!"; | 319 DLOG(ERROR) << "Received a buffer without timestamps!"; |
| 318 decode_cb.Run(kDecodeError); | 320 decode_cb.Run(kDecodeError); |
| 319 return; | 321 return; |
| 320 } | 322 } |
| 321 | 323 |
| 322 // Apply the necessary codec delay. | |
| 323 if (start_input_timestamp_ == kNoTimestamp()) | |
| 324 start_input_timestamp_ = input->timestamp(); | |
| 325 if (!discard_helper_->initialized() && | |
| 326 input->timestamp() == start_input_timestamp_) { | |
| 327 discard_helper_->Reset(config_.codec_delay()); | |
| 328 } | |
| 329 | |
| 330 scoped_refptr<AudioBuffer> output_buffer; | 324 scoped_refptr<AudioBuffer> output_buffer; |
| 331 | 325 |
| 332 if (!Decode(input, &output_buffer)) { | 326 if (!Decode(input, &output_buffer)) { |
| 333 decode_cb.Run(kDecodeError); | 327 decode_cb.Run(kDecodeError); |
| 334 return; | 328 return; |
| 335 } | 329 } |
| 336 | 330 |
| 337 if (output_buffer.get()) { | 331 if (output_buffer.get()) { |
| 338 output_cb_.Run(output_buffer); | 332 output_cb_.Run(output_buffer); |
| 339 } | 333 } |
| (...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 413 } | 407 } |
| 414 | 408 |
| 415 status = opus_multistream_decoder_ctl( | 409 status = opus_multistream_decoder_ctl( |
| 416 opus_decoder_, OPUS_SET_GAIN(opus_extra_data.gain_db)); | 410 opus_decoder_, OPUS_SET_GAIN(opus_extra_data.gain_db)); |
| 417 if (status != OPUS_OK) { | 411 if (status != OPUS_OK) { |
| 418 DLOG(ERROR) << "Failed to set OPUS header gain; status=" | 412 DLOG(ERROR) << "Failed to set OPUS header gain; status=" |
| 419 << opus_strerror(status); | 413 << opus_strerror(status); |
| 420 return false; | 414 return false; |
| 421 } | 415 } |
| 422 | 416 |
| 423 discard_helper_.reset( | 417 ResetTimestampState(); |
| 424 new AudioDiscardHelper(config_.samples_per_second(), 0)); | |
| 425 start_input_timestamp_ = kNoTimestamp(); | |
| 426 return true; | 418 return true; |
| 427 } | 419 } |
| 428 | 420 |
| 429 void OpusAudioDecoder::CloseDecoder() { | 421 void OpusAudioDecoder::CloseDecoder() { |
| 430 if (opus_decoder_) { | 422 if (opus_decoder_) { |
| 431 opus_multistream_decoder_destroy(opus_decoder_); | 423 opus_multistream_decoder_destroy(opus_decoder_); |
| 432 opus_decoder_ = NULL; | 424 opus_decoder_ = nullptr; |
| 433 } | 425 } |
| 434 } | 426 } |
| 435 | 427 |
| 436 void OpusAudioDecoder::ResetTimestampState() { | 428 void OpusAudioDecoder::ResetTimestampState() { |
| 437 discard_helper_->Reset( | 429 start_timestamp_ = |
| 438 discard_helper_->TimeDeltaToFrames(config_.seek_preroll())); | 430 FramesToTimeDelta(config_.codec_delay(), config_.samples_per_second()); |
| 431 discard_helper_.reset( | |
| 432 new AudioDiscardHelper(config_.samples_per_second(), 0)); | |
| 439 } | 433 } |
| 440 | 434 |
| 441 bool OpusAudioDecoder::Decode(const scoped_refptr<DecoderBuffer>& input, | 435 bool OpusAudioDecoder::Decode(const scoped_refptr<DecoderBuffer>& input, |
| 442 scoped_refptr<AudioBuffer>* output_buffer) { | 436 scoped_refptr<AudioBuffer>* output_buffer) { |
| 443 // Allocate a buffer for the output samples. | 437 // Allocate a buffer for the output samples. |
| 444 *output_buffer = AudioBuffer::CreateBuffer( | 438 *output_buffer = AudioBuffer::CreateBuffer( |
| 445 config_.sample_format(), | 439 config_.sample_format(), |
| 446 config_.channel_layout(), | 440 config_.channel_layout(), |
| 447 ChannelLayoutToChannelCount(config_.channel_layout()), | 441 ChannelLayoutToChannelCount(config_.channel_layout()), |
| 448 config_.samples_per_second(), | 442 config_.samples_per_second(), |
| (...skipping 21 matching lines...) Expand all Loading... | |
| 470 << " status: " << opus_strerror(frames_decoded); | 464 << " status: " << opus_strerror(frames_decoded); |
| 471 return false; | 465 return false; |
| 472 } | 466 } |
| 473 | 467 |
| 474 // Trim off any extraneous allocation. | 468 // Trim off any extraneous allocation. |
| 475 DCHECK_LE(frames_decoded, output_buffer->get()->frame_count()); | 469 DCHECK_LE(frames_decoded, output_buffer->get()->frame_count()); |
| 476 const int trim_frames = output_buffer->get()->frame_count() - frames_decoded; | 470 const int trim_frames = output_buffer->get()->frame_count() - frames_decoded; |
| 477 if (trim_frames > 0) | 471 if (trim_frames > 0) |
| 478 output_buffer->get()->TrimEnd(trim_frames); | 472 output_buffer->get()->TrimEnd(trim_frames); |
| 479 | 473 |
| 474 // Workaround for the lack of proper pre-skip support in MediaSource; if this | |
| 475 // is the first packet and the timestamp is zero, strip codec delay. | |
|
wolenetz
2015/07/29 21:57:27
Hmm. In MSE, the fix could be simpler: on config c
DaleCurtis
2015/07/30 01:28:14
That understanding is not correct, only the first
DaleCurtis
2015/07/31 02:41:00
Turns out my understanding was totally wrong. It i
wolenetz
2015/08/06 22:21:27
No worries. Thanks for following-up on this point
| |
| 476 // TODO(chcunningham): Remove this hack, http://crbug.com/509894 | |
| 477 if (input->timestamp() < start_timestamp_ && | |
| 478 input->discard_padding().first == base::TimeDelta()) { | |
| 479 input->set_discard_padding(std::make_pair( | |
| 480 std::min(input->duration(), start_timestamp_ - input->timestamp()), | |
| 481 base::TimeDelta())); | |
| 482 } | |
| 483 | |
| 480 // Handles discards and timestamping. Discard the buffer if more data needed. | 484 // Handles discards and timestamping. Discard the buffer if more data needed. |
| 481 if (!discard_helper_->ProcessBuffers(input, *output_buffer)) | 485 if (!discard_helper_->ProcessBuffers(input, *output_buffer)) |
| 482 *output_buffer = NULL; | 486 *output_buffer = nullptr; |
| 483 | 487 |
| 484 return true; | 488 return true; |
| 485 } | 489 } |
| 486 | 490 |
| 487 } // namespace media | 491 } // namespace media |
| OLD | NEW |