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

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

Issue 1260193005: Fix incorrect opus seek preroll and flaky pre-skip removal. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Add test cases. Created 5 years, 4 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
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_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
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698