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/ffmpeg_audio_decoder.h" | 5 #include "media/filters/ffmpeg_audio_decoder.h" |
| 6 | 6 |
| 7 #include <stdint.h> | 7 #include <stdint.h> |
| 8 | 8 |
| 9 #include "base/callback_helpers.h" | 9 #include "base/callback_helpers.h" |
| 10 #include "base/single_thread_task_runner.h" | 10 #include "base/single_thread_task_runner.h" |
| (...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 89 // we need to take this into consideration. | 89 // we need to take this into consideration. |
| 90 int buffer_size_in_bytes = av_samples_get_buffer_size( | 90 int buffer_size_in_bytes = av_samples_get_buffer_size( |
| 91 &frame->linesize[0], channels, frame->nb_samples, format, | 91 &frame->linesize[0], channels, frame->nb_samples, format, |
| 92 0 /* align, use ffmpeg default */); | 92 0 /* align, use ffmpeg default */); |
| 93 // Check for errors from av_samples_get_buffer_size(). | 93 // Check for errors from av_samples_get_buffer_size(). |
| 94 if (buffer_size_in_bytes < 0) | 94 if (buffer_size_in_bytes < 0) |
| 95 return buffer_size_in_bytes; | 95 return buffer_size_in_bytes; |
| 96 int frames_required = buffer_size_in_bytes / bytes_per_channel / channels; | 96 int frames_required = buffer_size_in_bytes / bytes_per_channel / channels; |
| 97 DCHECK_GE(frames_required, frame->nb_samples); | 97 DCHECK_GE(frames_required, frame->nb_samples); |
| 98 | 98 |
| 99 bool is_opus_discrete = false; | |
|
DaleCurtis
2017/03/29 22:46:55
Isn't this information already in the AudioDecoder
flim-chromium
2017/03/30 19:35:10
True, but this is a static function and the non-st
DaleCurtis
2017/03/30 20:22:21
Ah right, this isn't like FFmpegVideoDecoder where
flim-chromium
2017/03/31 00:12:14
I believe so - it includes the additional check th
DaleCurtis
2017/03/31 19:06:53
I'm moving this to be a class method in https://co
flim-chromium
2017/04/05 18:19:34
thanks, this has been updated to use config_
| |
| 100 if (s->codec_id == AV_CODEC_ID_OPUS && s->extradata_size >= 19) { | |
| 101 int mapping_family = s->extradata[18]; | |
| 102 is_opus_discrete = mapping_family == 2; | |
| 103 } | |
| 99 ChannelLayout channel_layout = | 104 ChannelLayout channel_layout = |
| 100 ChannelLayoutToChromeChannelLayout(s->channel_layout, s->channels); | 105 is_opus_discrete && channels > 8 |
| 106 ? CHANNEL_LAYOUT_DISCRETE | |
| 107 : ChannelLayoutToChromeChannelLayout(s->channel_layout, s->channels); | |
| 101 | 108 |
| 102 if (channel_layout == CHANNEL_LAYOUT_UNSUPPORTED) { | 109 if (channel_layout == CHANNEL_LAYOUT_UNSUPPORTED) { |
| 103 DLOG(ERROR) << "Unsupported channel layout."; | 110 DLOG(ERROR) << "Unsupported channel layout."; |
| 104 return AVERROR(EINVAL); | 111 return AVERROR(EINVAL); |
| 105 } | 112 } |
| 106 | 113 |
| 107 scoped_refptr<AudioBuffer> buffer = AudioBuffer::CreateBuffer( | 114 scoped_refptr<AudioBuffer> buffer = AudioBuffer::CreateBuffer( |
| 108 sample_format, channel_layout, channels, s->sample_rate, frames_required); | 115 sample_format, channel_layout, channels, s->sample_rate, frames_required); |
| 109 | 116 |
| 110 // Initialize the data[] and extended_data[] fields to point into the memory | 117 // Initialize the data[] and extended_data[] fields to point into the memory |
| (...skipping 186 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 297 scoped_refptr<AudioBuffer> output; | 304 scoped_refptr<AudioBuffer> output; |
| 298 | 305 |
| 299 bool config_changed = false; | 306 bool config_changed = false; |
| 300 if (frame_decoded) { | 307 if (frame_decoded) { |
| 301 const int channels = DetermineChannels(av_frame_.get()); | 308 const int channels = DetermineChannels(av_frame_.get()); |
| 302 ChannelLayout channel_layout = ChannelLayoutToChromeChannelLayout( | 309 ChannelLayout channel_layout = ChannelLayoutToChromeChannelLayout( |
| 303 codec_context_->channel_layout, codec_context_->channels); | 310 codec_context_->channel_layout, codec_context_->channels); |
| 304 | 311 |
| 305 bool is_sample_rate_change = | 312 bool is_sample_rate_change = |
| 306 av_frame_->sample_rate != config_.samples_per_second(); | 313 av_frame_->sample_rate != config_.samples_per_second(); |
| 307 bool is_config_stale = | 314 bool is_config_stale = is_sample_rate_change || |
| 308 is_sample_rate_change || | 315 channels != config_.channels() || |
| 309 channels != ChannelLayoutToChannelCount(config_.channel_layout()) || | 316 av_frame_->format != av_sample_format_; |
| 310 av_frame_->format != av_sample_format_; | |
| 311 | 317 |
| 312 // Only consider channel layout changes for AAC. | 318 // Only consider channel layout changes for AAC. |
| 313 // TODO(tguilbert, dalecurtis): Due to http://crbug.com/600538 we need to | 319 // TODO(tguilbert, dalecurtis): Due to http://crbug.com/600538 we need to |
| 314 // allow channel layout changes for the moment. See if ffmpeg is fixable. | 320 // allow channel layout changes for the moment. See if ffmpeg is fixable. |
| 315 if (config_.codec() == kCodecAAC) | 321 if (config_.codec() == kCodecAAC) |
| 316 is_config_stale |= channel_layout != config_.channel_layout(); | 322 is_config_stale |= channel_layout != config_.channel_layout(); |
| 317 | 323 |
| 318 if (is_config_stale) { | 324 if (is_config_stale) { |
| 319 // Only allow midstream configuration changes for AAC. Sample format is | 325 // Only allow midstream configuration changes for AAC. Sample format is |
| 320 // not expected to change between AAC profiles. | 326 // not expected to change between AAC profiles. |
| 321 if (config_.codec() == kCodecAAC && | 327 if (config_.codec() == kCodecAAC && |
| 322 av_frame_->format == av_sample_format_) { | 328 av_frame_->format == av_sample_format_) { |
| 323 MEDIA_LOG(DEBUG, media_log_) | 329 MEDIA_LOG(DEBUG, media_log_) |
| 324 << " Detected AAC midstream configuration change" | 330 << " Detected AAC midstream configuration change" |
| 325 << " PTS:" << buffer->timestamp().InMicroseconds() | 331 << " PTS:" << buffer->timestamp().InMicroseconds() |
| 326 << " Sample Rate: " << av_frame_->sample_rate << " vs " | 332 << " Sample Rate: " << av_frame_->sample_rate << " vs " |
| 327 << config_.samples_per_second() | 333 << config_.samples_per_second() |
| 328 << ", ChannelLayout: " << channel_layout << " vs " | 334 << ", ChannelLayout: " << channel_layout << " vs " |
| 329 << config_.channel_layout() << ", Channels: " << channels | 335 << config_.channel_layout() << ", Channels: " << channels |
| 330 << " vs " | 336 << " vs " << config_.channels(); |
| 331 << ChannelLayoutToChannelCount(config_.channel_layout()); | |
| 332 config_.Initialize(config_.codec(), config_.sample_format(), | 337 config_.Initialize(config_.codec(), config_.sample_format(), |
| 333 channel_layout, av_frame_->sample_rate, | 338 channel_layout, av_frame_->sample_rate, |
| 334 config_.extra_data(), config_.encryption_scheme(), | 339 config_.extra_data(), config_.encryption_scheme(), |
| 335 config_.seek_preroll(), config_.codec_delay()); | 340 config_.seek_preroll(), config_.codec_delay()); |
| 336 config_changed = true; | 341 config_changed = true; |
| 337 if (is_sample_rate_change) | 342 if (is_sample_rate_change) |
| 338 ResetTimestampState(); | 343 ResetTimestampState(); |
| 339 } else { | 344 } else { |
| 340 MEDIA_LOG(ERROR, media_log_) | 345 MEDIA_LOG(ERROR, media_log_) |
| 341 << "Unsupported midstream configuration change!" | 346 << "Unsupported midstream configuration change!" |
| 342 << " Sample Rate: " << av_frame_->sample_rate << " vs " | 347 << " Sample Rate: " << av_frame_->sample_rate << " vs " |
| 343 << config_.samples_per_second() << ", Channels: " << channels | 348 << config_.samples_per_second() << ", Channels: " << channels |
| 344 << " vs " << ChannelLayoutToChannelCount(config_.channel_layout()) | 349 << " vs " << config_.channels() |
| 345 << ", Sample Format: " << av_frame_->format << " vs " | 350 << ", Sample Format: " << av_frame_->format << " vs " |
| 346 << av_sample_format_; | 351 << av_sample_format_; |
| 347 // This is an unrecoverable error, so bail out. | 352 // This is an unrecoverable error, so bail out. |
| 348 av_frame_unref(av_frame_.get()); | 353 av_frame_unref(av_frame_.get()); |
| 349 return false; | 354 return false; |
| 350 } | 355 } |
| 351 } | 356 } |
| 352 | 357 |
| 353 // Get the AudioBuffer that the data was decoded into. Adjust the number | 358 // Get the AudioBuffer that the data was decoded into. Adjust the number |
| 354 // of frames, in case fewer than requested were actually decoded. | 359 // of frames, in case fewer than requested were actually decoded. |
| 355 output = reinterpret_cast<AudioBuffer*>( | 360 output = reinterpret_cast<AudioBuffer*>( |
| 356 av_buffer_get_opaque(av_frame_->buf[0])); | 361 av_buffer_get_opaque(av_frame_->buf[0])); |
| 357 | 362 |
| 358 DCHECK_EQ(ChannelLayoutToChannelCount(config_.channel_layout()), | 363 DCHECK_EQ(config_.channels(), output->channel_count()); |
| 359 output->channel_count()); | |
| 360 const int unread_frames = output->frame_count() - av_frame_->nb_samples; | 364 const int unread_frames = output->frame_count() - av_frame_->nb_samples; |
| 361 DCHECK_GE(unread_frames, 0); | 365 DCHECK_GE(unread_frames, 0); |
| 362 if (unread_frames > 0) | 366 if (unread_frames > 0) |
| 363 output->TrimEnd(unread_frames); | 367 output->TrimEnd(unread_frames); |
| 364 av_frame_unref(av_frame_.get()); | 368 av_frame_unref(av_frame_.get()); |
| 365 } | 369 } |
| 366 | 370 |
| 367 // WARNING: |av_frame_| no longer has valid data at this point. | 371 // WARNING: |av_frame_| no longer has valid data at this point. |
| 368 const int decoded_frames = frame_decoded ? output->frame_count() : 0; | 372 const int decoded_frames = frame_decoded ? output->frame_count() : 0; |
| 369 if (IsEndOfStream(result, decoded_frames, buffer)) { | 373 if (IsEndOfStream(result, decoded_frames, buffer)) { |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 414 << codec_context_->codec_id; | 418 << codec_context_->codec_id; |
| 415 ReleaseFFmpegResources(); | 419 ReleaseFFmpegResources(); |
| 416 state_ = kUninitialized; | 420 state_ = kUninitialized; |
| 417 return false; | 421 return false; |
| 418 } | 422 } |
| 419 | 423 |
| 420 // Success! | 424 // Success! |
| 421 av_frame_.reset(av_frame_alloc()); | 425 av_frame_.reset(av_frame_alloc()); |
| 422 av_sample_format_ = codec_context_->sample_fmt; | 426 av_sample_format_ = codec_context_->sample_fmt; |
| 423 | 427 |
| 424 if (codec_context_->channels != | 428 if (codec_context_->channels != config_.channels()) { |
| 425 ChannelLayoutToChannelCount(config_.channel_layout())) { | 429 DLOG(ERROR) << "Audio configuration specified " << config_.channels() |
| 426 DLOG(ERROR) << "Audio configuration specified " | |
| 427 << ChannelLayoutToChannelCount(config_.channel_layout()) | |
| 428 << " channels, but FFmpeg thinks the file contains " | 430 << " channels, but FFmpeg thinks the file contains " |
| 429 << codec_context_->channels << " channels"; | 431 << codec_context_->channels << " channels"; |
| 430 ReleaseFFmpegResources(); | 432 ReleaseFFmpegResources(); |
| 431 state_ = kUninitialized; | 433 state_ = kUninitialized; |
| 432 return false; | 434 return false; |
| 433 } | 435 } |
| 434 | 436 |
| 435 ResetTimestampState(); | 437 ResetTimestampState(); |
| 436 return true; | 438 return true; |
| 437 } | 439 } |
| 438 | 440 |
| 439 void FFmpegAudioDecoder::ResetTimestampState() { | 441 void FFmpegAudioDecoder::ResetTimestampState() { |
| 440 // Opus codec delay is handled by ffmpeg. | 442 // Opus codec delay is handled by ffmpeg. |
| 441 const int codec_delay = | 443 const int codec_delay = |
| 442 config_.codec() == kCodecOpus ? 0 : config_.codec_delay(); | 444 config_.codec() == kCodecOpus ? 0 : config_.codec_delay(); |
| 443 discard_helper_.reset( | 445 discard_helper_.reset( |
| 444 new AudioDiscardHelper(config_.samples_per_second(), codec_delay, | 446 new AudioDiscardHelper(config_.samples_per_second(), codec_delay, |
| 445 config_.codec() == kCodecVorbis)); | 447 config_.codec() == kCodecVorbis)); |
| 446 discard_helper_->Reset(codec_delay); | 448 discard_helper_->Reset(codec_delay); |
| 447 } | 449 } |
| 448 | 450 |
| 449 } // namespace media | 451 } // namespace media |
| OLD | NEW |