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

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

Issue 259453003: Introduce AudioDiscardHelper. Refactor audio decoders to use it. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fix x64 type. Created 6 years, 7 months 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
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_timestamp_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 uint16 ReadLE16(const uint8* data, size_t data_size, int read_offset) { 22 static uint16 ReadLE16(const uint8* data, size_t data_size, int read_offset) {
23 uint16 value = 0; 23 uint16 value = 0;
24 DCHECK_LE(read_offset + sizeof(value), data_size); 24 DCHECK_LE(read_offset + sizeof(value), data_size);
25 memcpy(&value, data + read_offset, sizeof(value)); 25 memcpy(&value, data + read_offset, sizeof(value));
26 return base::ByteSwapToLE16(value); 26 return base::ByteSwapToLE16(value);
27 } 27 }
28 28
29 static int TimeDeltaToAudioFrames(base::TimeDelta time_delta,
30 int frame_rate) {
31 return std::ceil(time_delta.InSecondsF() * frame_rate);
32 }
33
34 // The Opus specification is part of IETF RFC 6716: 29 // The Opus specification is part of IETF RFC 6716:
35 // http://tools.ietf.org/html/rfc6716 30 // http://tools.ietf.org/html/rfc6716
36 31
37 // Opus uses Vorbis channel mapping, and Vorbis channel mapping specifies 32 // Opus uses Vorbis channel mapping, and Vorbis channel mapping specifies
38 // mappings for up to 8 channels. This information is part of the Vorbis I 33 // mappings for up to 8 channels. This information is part of the Vorbis I
39 // Specification: 34 // Specification:
40 // http://www.xiph.org/vorbis/doc/Vorbis_I_spec.html 35 // http://www.xiph.org/vorbis/doc/Vorbis_I_spec.html
41 static const int kMaxVorbisChannels = 8; 36 static const int kMaxVorbisChannels = 8;
42 37
43 // Maximum packet size used in Xiph's opusdec and FFmpeg's libopusdec. 38 // Maximum packet size used in Xiph's opusdec and FFmpeg's libopusdec.
(...skipping 200 matching lines...) Expand 10 before | Expand all | Expand 10 after
244 239
245 for (int i = 0; i < extra_data->channels; ++i) 240 for (int i = 0; i < extra_data->channels; ++i)
246 extra_data->stream_map[i] = *(data + kOpusExtraDataStreamMapOffset + i); 241 extra_data->stream_map[i] = *(data + kOpusExtraDataStreamMapOffset + i);
247 return true; 242 return true;
248 } 243 }
249 244
250 OpusAudioDecoder::OpusAudioDecoder( 245 OpusAudioDecoder::OpusAudioDecoder(
251 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) 246 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner)
252 : task_runner_(task_runner), 247 : task_runner_(task_runner),
253 opus_decoder_(NULL), 248 opus_decoder_(NULL),
254 last_input_timestamp_(kNoTimestamp()),
255 frames_to_discard_(0),
256 start_input_timestamp_(kNoTimestamp()) {} 249 start_input_timestamp_(kNoTimestamp()) {}
257 250
258 void OpusAudioDecoder::Initialize(const AudioDecoderConfig& config, 251 void OpusAudioDecoder::Initialize(const AudioDecoderConfig& config,
259 const PipelineStatusCB& status_cb) { 252 const PipelineStatusCB& status_cb) {
260 DCHECK(task_runner_->BelongsToCurrentThread()); 253 DCHECK(task_runner_->BelongsToCurrentThread());
261 PipelineStatusCB initialize_cb = BindToCurrentLoop(status_cb); 254 PipelineStatusCB initialize_cb = BindToCurrentLoop(status_cb);
262 255
263 config_ = config; 256 config_ = config;
264 257
265 if (!ConfigureDecoder()) { 258 if (!ConfigureDecoder()) {
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
309 302
310 // Libopus does not buffer output. Decoding is complete when an end of stream 303 // Libopus does not buffer output. Decoding is complete when an end of stream
311 // input buffer is received. 304 // input buffer is received.
312 if (input->end_of_stream()) { 305 if (input->end_of_stream()) {
313 decode_cb.Run(kOk, AudioBuffer::CreateEOSBuffer()); 306 decode_cb.Run(kOk, AudioBuffer::CreateEOSBuffer());
314 return; 307 return;
315 } 308 }
316 309
317 // Make sure we are notified if http://crbug.com/49709 returns. Issue also 310 // Make sure we are notified if http://crbug.com/49709 returns. Issue also
318 // occurs with some damaged files. 311 // occurs with some damaged files.
319 if (input->timestamp() == kNoTimestamp() && 312 if (input->timestamp() == kNoTimestamp()) {
320 output_timestamp_helper_->base_timestamp() == kNoTimestamp()) {
321 DLOG(ERROR) << "Received a buffer without timestamps!"; 313 DLOG(ERROR) << "Received a buffer without timestamps!";
322 decode_cb.Run(kDecodeError, NULL); 314 decode_cb.Run(kDecodeError, NULL);
323 return; 315 return;
324 } 316 }
325 317
326 if (last_input_timestamp_ != kNoTimestamp() &&
327 input->timestamp() != kNoTimestamp() &&
328 input->timestamp() < last_input_timestamp_) {
329 base::TimeDelta diff = input->timestamp() - last_input_timestamp_;
330 DLOG(ERROR) << "Input timestamps are not monotonically increasing! "
331 << " ts " << input->timestamp().InMicroseconds() << " us"
332 << " diff " << diff.InMicroseconds() << " us";
333 decode_cb.Run(kDecodeError, NULL);
334 return;
335 }
336
337 // Apply the necessary codec delay. 318 // Apply the necessary codec delay.
338 if (start_input_timestamp_ == kNoTimestamp()) 319 if (start_input_timestamp_ == kNoTimestamp())
339 start_input_timestamp_ = input->timestamp(); 320 start_input_timestamp_ = input->timestamp();
340 if (last_input_timestamp_ == kNoTimestamp() && 321 if (!discard_helper_->initialized() &&
341 input->timestamp() == start_input_timestamp_) { 322 input->timestamp() == start_input_timestamp_) {
342 frames_to_discard_ = config_.codec_delay(); 323 discard_helper_->Reset(config_.codec_delay());
343 } 324 }
344 325
345 last_input_timestamp_ = input->timestamp();
346
347 scoped_refptr<AudioBuffer> output_buffer; 326 scoped_refptr<AudioBuffer> output_buffer;
348 327
349 if (!Decode(input, &output_buffer)) { 328 if (!Decode(input, &output_buffer)) {
350 decode_cb.Run(kDecodeError, NULL); 329 decode_cb.Run(kDecodeError, NULL);
351 return; 330 return;
352 } 331 }
353 332
354 if (output_buffer.get()) { 333 if (output_buffer.get()) {
355 // Execute callback to return the decoded audio. 334 // Execute callback to return the decoded audio.
356 decode_cb.Run(kOk, output_buffer); 335 decode_cb.Run(kOk, output_buffer);
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after
432 } 411 }
433 412
434 status = opus_multistream_decoder_ctl( 413 status = opus_multistream_decoder_ctl(
435 opus_decoder_, OPUS_SET_GAIN(opus_extra_data.gain_db)); 414 opus_decoder_, OPUS_SET_GAIN(opus_extra_data.gain_db));
436 if (status != OPUS_OK) { 415 if (status != OPUS_OK) {
437 DLOG(ERROR) << "Failed to set OPUS header gain; status=" 416 DLOG(ERROR) << "Failed to set OPUS header gain; status="
438 << opus_strerror(status); 417 << opus_strerror(status);
439 return false; 418 return false;
440 } 419 }
441 420
442 output_timestamp_helper_.reset( 421 discard_helper_.reset(new AudioDiscardHelper(config_.samples_per_second()));
443 new AudioTimestampHelper(config_.samples_per_second()));
444 start_input_timestamp_ = kNoTimestamp(); 422 start_input_timestamp_ = kNoTimestamp();
445 return true; 423 return true;
446 } 424 }
447 425
448 void OpusAudioDecoder::CloseDecoder() { 426 void OpusAudioDecoder::CloseDecoder() {
449 if (opus_decoder_) { 427 if (opus_decoder_) {
450 opus_multistream_decoder_destroy(opus_decoder_); 428 opus_multistream_decoder_destroy(opus_decoder_);
451 opus_decoder_ = NULL; 429 opus_decoder_ = NULL;
452 } 430 }
453 } 431 }
454 432
455 void OpusAudioDecoder::ResetTimestampState() { 433 void OpusAudioDecoder::ResetTimestampState() {
456 output_timestamp_helper_->SetBaseTimestamp(kNoTimestamp()); 434 discard_helper_->Reset(
457 last_input_timestamp_ = kNoTimestamp(); 435 discard_helper_->TimeDeltaToFrames(config_.seek_preroll()));
458 frames_to_discard_ = TimeDeltaToAudioFrames(config_.seek_preroll(),
459 config_.samples_per_second());
460 } 436 }
461 437
462 bool OpusAudioDecoder::Decode(const scoped_refptr<DecoderBuffer>& input, 438 bool OpusAudioDecoder::Decode(const scoped_refptr<DecoderBuffer>& input,
463 scoped_refptr<AudioBuffer>* output_buffer) { 439 scoped_refptr<AudioBuffer>* output_buffer) {
464 // Allocate a buffer for the output samples. 440 // Allocate a buffer for the output samples.
465 *output_buffer = AudioBuffer::CreateBuffer( 441 *output_buffer = AudioBuffer::CreateBuffer(
466 config_.sample_format(), 442 config_.sample_format(),
467 config_.channel_layout(), 443 config_.channel_layout(),
468 ChannelLayoutToChannelCount(config_.channel_layout()), 444 ChannelLayoutToChannelCount(config_.channel_layout()),
469 config_.samples_per_second(), 445 config_.samples_per_second(),
(...skipping 15 matching lines...) Expand all
485 461
486 if (frames_decoded < 0) { 462 if (frames_decoded < 0) {
487 DLOG(ERROR) << "opus_multistream_decode failed for" 463 DLOG(ERROR) << "opus_multistream_decode failed for"
488 << " timestamp: " << input->timestamp().InMicroseconds() 464 << " timestamp: " << input->timestamp().InMicroseconds()
489 << " us, duration: " << input->duration().InMicroseconds() 465 << " us, duration: " << input->duration().InMicroseconds()
490 << " us, packet size: " << input->data_size() << " bytes with" 466 << " us, packet size: " << input->data_size() << " bytes with"
491 << " status: " << opus_strerror(frames_decoded); 467 << " status: " << opus_strerror(frames_decoded);
492 return false; 468 return false;
493 } 469 }
494 470
495 if (output_timestamp_helper_->base_timestamp() == kNoTimestamp() &&
496 !input->end_of_stream()) {
497 DCHECK(input->timestamp() != kNoTimestamp());
498 output_timestamp_helper_->SetBaseTimestamp(input->timestamp());
499 }
500
501 // Trim off any extraneous allocation. 471 // Trim off any extraneous allocation.
502 DCHECK_LE(frames_decoded, output_buffer->get()->frame_count()); 472 DCHECK_LE(frames_decoded, output_buffer->get()->frame_count());
503 const int trim_frames = output_buffer->get()->frame_count() - frames_decoded; 473 const int trim_frames = output_buffer->get()->frame_count() - frames_decoded;
504 if (trim_frames > 0) 474 if (trim_frames > 0)
505 output_buffer->get()->TrimEnd(trim_frames); 475 output_buffer->get()->TrimEnd(trim_frames);
506 476
507 // Handle frame discard and trimming. 477 // Handles discards and timestamping. Discard the buffer if more data needed.
508 int frames_to_output = frames_decoded; 478 if (!discard_helper_->ProcessBuffers(input, *output_buffer))
509 if (frames_decoded > frames_to_discard_) { 479 *output_buffer = NULL;
510 if (frames_to_discard_ > 0) {
511 output_buffer->get()->TrimStart(frames_to_discard_);
512 frames_to_output -= frames_to_discard_;
513 frames_to_discard_ = 0;
514 }
515 if (input->discard_padding().InMicroseconds() > 0) {
516 int discard_padding = TimeDeltaToAudioFrames(
517 input->discard_padding(), config_.samples_per_second());
518 if (discard_padding < 0 || discard_padding > frames_to_output) {
519 DVLOG(1) << "Invalid file. Incorrect discard padding value.";
520 return false;
521 }
522 output_buffer->get()->TrimEnd(discard_padding);
523 frames_to_output -= discard_padding;
524 } else {
525 DCHECK_EQ(input->discard_padding().InMicroseconds(), 0);
526 }
527 } else {
528 frames_to_discard_ -= frames_to_output;
529 frames_to_output = 0;
530 }
531 480
532 // Discard the buffer to indicate we need more data.
533 if (!frames_to_output) {
534 *output_buffer = NULL;
535 return true;
536 }
537
538 // Assign timestamp and duration to the buffer.
539 output_buffer->get()->set_timestamp(output_timestamp_helper_->GetTimestamp());
540 output_buffer->get()->set_duration(
541 output_timestamp_helper_->GetFrameDuration(frames_to_output));
542 output_timestamp_helper_->AddFrames(frames_to_output);
543 return true; 481 return true;
544 } 482 }
545 483
546 } // namespace media 484 } // 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