Chromium Code Reviews| Index: media/renderers/audio_renderer_impl.cc | 
| diff --git a/media/renderers/audio_renderer_impl.cc b/media/renderers/audio_renderer_impl.cc | 
| index 0d417c405a1fe42d806285600349a74505d2e021..a12cb7fdb2562aa65fa7793400dfc02e2ebd548b 100644 | 
| --- a/media/renderers/audio_renderer_impl.cc | 
| +++ b/media/renderers/audio_renderer_impl.cc | 
| @@ -24,6 +24,7 @@ | 
| #include "media/base/bind_to_current_loop.h" | 
| #include "media/base/channel_mixing_matrix.h" | 
| #include "media/base/demuxer_stream.h" | 
| +#include "media/base/media_client.h" | 
| #include "media/base/media_log.h" | 
| #include "media/base/media_switches.h" | 
| #include "media/base/renderer_client.h" | 
| @@ -33,6 +34,8 @@ | 
| namespace media { | 
| +static const int kMaxFramesPerCompressedAudioBuffer = 4096; | 
| 
 
DaleCurtis
2017/06/15 21:46:33
Needs explanation; possibly should be stored in Au
 
AndyWu
2017/08/02 01:43:41
Done.
 
 | 
| + | 
| AudioRendererImpl::AudioRendererImpl( | 
| const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, | 
| media::AudioRendererSink* sink, | 
| @@ -59,6 +62,8 @@ AudioRendererImpl::AudioRendererImpl( | 
| received_end_of_stream_(false), | 
| rendered_end_of_stream_(false), | 
| is_suspending_(false), | 
| + is_passthrough_(false), | 
| + last_reported_media_time_(kNoTimestamp), | 
| weak_factory_(this) { | 
| DCHECK(create_audio_decoders_cb_); | 
| // Tests may not have a power monitor. | 
| @@ -179,6 +184,7 @@ void AudioRendererImpl::SetMediaTime(base::TimeDelta time) { | 
| last_render_time_ = stop_rendering_time_ = base::TimeTicks(); | 
| first_packet_timestamp_ = kNoTimestamp; | 
| audio_clock_.reset(new AudioClock(time, audio_parameters_.sample_rate())); | 
| + last_reported_media_time_ = kNoTimestamp; | 
| } | 
| base::TimeDelta AudioRendererImpl::CurrentMediaTime() { | 
| @@ -194,7 +200,14 @@ base::TimeDelta AudioRendererImpl::CurrentMediaTime() { | 
| current_media_time = audio_clock_->back_timestamp(); | 
| } | 
| - return current_media_time; | 
| + // Clamp current media time to the last reported value, this prevents higher | 
| 
 
DaleCurtis
2017/06/15 21:46:33
We already do this in PipelineImpl.
 
AndyWu
2017/08/02 01:43:41
Hi Chris, do you agree to remove this logic?
 
chcunningham
2017/08/04 19:26:40
Yep, should be fine. It used to live here, but had
 
AndyWu
2017/08/04 21:45:52
Thanks for your feedback.
 
 | 
| + // level clients from seeing time go backwards. This may happen when seeking | 
| + // a passthrough audio stream, since we are unable to trim a compressed audio | 
| + // buffer. | 
| + if (last_reported_media_time_ < current_media_time) | 
| + last_reported_media_time_ = current_media_time; | 
| + | 
| + return last_reported_media_time_; | 
| } | 
| bool AudioRendererImpl::GetWallClockTimes( | 
| @@ -369,6 +382,10 @@ void AudioRendererImpl::Initialize(DemuxerStream* stream, | 
| auto output_device_info = sink_->GetOutputDeviceInfo(); | 
| const AudioParameters& hw_params = output_device_info.output_params(); | 
| + AudioCodec codec = stream->audio_decoder_config().codec(); | 
| + MediaClient* media_client = GetMediaClient(); | 
| 
 
DaleCurtis
2017/06/15 21:46:33
if (auto* mc = GetMediaClient())
  is_passthrough_
 
AndyWu
2017/08/02 01:43:41
Done.
 
 | 
| + is_passthrough_ = | 
| + media_client && media_client->IsSupportedBitstreamAudioCodec(codec); | 
| expecting_config_changes_ = stream->SupportsConfigChanges(); | 
| bool use_stream_params = !expecting_config_changes_ || !hw_params.IsValid() || | 
| @@ -381,7 +398,25 @@ void AudioRendererImpl::Initialize(DemuxerStream* stream, | 
| use_stream_params = false; | 
| } | 
| - if (use_stream_params) { | 
| + if (is_passthrough_) { | 
| + AudioParameters::Format format = AudioParameters::AUDIO_FAKE; | 
| + if (codec == kCodecAC3) { | 
| + format = AudioParameters::AUDIO_BITSTREAM_AC3; | 
| + } else if (codec == kCodecEAC3) { | 
| + format = AudioParameters::AUDIO_BITSTREAM_EAC3; | 
| + } else { | 
| + NOTREACHED(); | 
| + } | 
| + | 
| + const int buffer_size = kMaxFramesPerCompressedAudioBuffer * | 
| + stream->audio_decoder_config().bytes_per_frame(); | 
| + | 
| + audio_parameters_.Reset( | 
| + format, stream->audio_decoder_config().channel_layout(), | 
| + stream->audio_decoder_config().samples_per_second(), | 
| + stream->audio_decoder_config().bits_per_channel(), buffer_size); | 
| + buffer_converter_.reset(); | 
| + } else if (use_stream_params) { | 
| // The actual buffer size is controlled via the size of the AudioBus | 
| // provided to Render(), but we should choose a value here based on hardware | 
| // parameters if possible since it affects the initial buffer size used by | 
| @@ -669,7 +704,19 @@ bool AudioRendererImpl::HandleDecodedBuffer_Locked( | 
| if (buffer->end_of_stream()) { | 
| received_end_of_stream_ = true; | 
| } else { | 
| - if (state_ == kPlaying) { | 
| + if (buffer->IsBitstreamFormat() && state_ == kPlaying) { | 
| + if (IsBeforeStartTime(buffer)) | 
| + return true; | 
| + | 
| + // Adjust the start time since we are unable to trim a compressed audio | 
| + // buffer. | 
| + if (buffer->timestamp() < start_timestamp_ && | 
| 
 
DaleCurtis
2017/06/15 21:46:33
I'd just skip this and seek to the first full buff
 
AndyWu
2017/08/02 01:43:41
Done.
 
 | 
| + (buffer->timestamp() + buffer->duration()) > start_timestamp_) { | 
| + start_timestamp_ = buffer->timestamp(); | 
| + audio_clock_.reset(new AudioClock(buffer->timestamp(), | 
| + audio_parameters_.sample_rate())); | 
| + } | 
| + } else if (state_ == kPlaying) { | 
| if (IsBeforeStartTime(buffer)) | 
| return true; | 
| @@ -768,6 +815,13 @@ void AudioRendererImpl::SetPlaybackRate(double playback_rate) { | 
| base::AutoLock auto_lock(lock_); | 
| + if (is_passthrough_ && playback_rate != 0 && playback_rate != 1) { | 
| + MEDIA_LOG(ERROR, media_log_) | 
| 
 
DaleCurtis
2017/06/15 21:46:32
WARNING or INFO, no need for ERROR.
 
AndyWu
2017/08/02 01:43:41
Done.
 
 | 
| + << "Unsupported playback rate when outputing compressed bitstream." | 
| + << " Playback Rate: " << playback_rate; | 
| + return; | 
| + } | 
| + | 
| // We have two cases here: | 
| // Play: current_playback_rate == 0 && playback_rate != 0 | 
| // Pause: current_playback_rate != 0 && playback_rate == 0 | 
| @@ -799,7 +853,7 @@ int AudioRendererImpl::Render(base::TimeDelta delay, | 
| base::TimeTicks delay_timestamp, | 
| int prior_frames_skipped, | 
| AudioBus* audio_bus) { | 
| - const int frames_requested = audio_bus->frames(); | 
| + int frames_requested = audio_bus->frames(); | 
| DVLOG(4) << __func__ << " delay:" << delay | 
| << " prior_frames_skipped:" << prior_frames_skipped | 
| << " frames_requested:" << frames_requested; | 
| @@ -838,9 +892,13 @@ int AudioRendererImpl::Render(base::TimeDelta delay, | 
| return 0; | 
| } | 
| - // Delay playback by writing silence if we haven't reached the first | 
| - // timestamp yet; this can occur if the video starts before the audio. | 
| - if (algorithm_->frames_buffered() > 0) { | 
| + if (is_passthrough_ && algorithm_->frames_buffered() > 0) { | 
| + frames_written += algorithm_->FillBuffer(audio_bus, 0, frames_requested, | 
| 
 
chcunningham
2017/06/14 20:03:08
special passthrough logic needs "why" documentatio
 
DaleCurtis
2017/06/15 21:46:33
FYI, this is going to be wrong in cases where the
 
AndyWu
2017/08/02 01:43:41
Done.
 
AndyWu
2017/08/02 01:43:41
Done.
 
 | 
| + playback_rate_); | 
| + frames_requested = frames_written; | 
| + } else if (algorithm_->frames_buffered() > 0) { | 
| + // Delay playback by writing silence if we haven't reached the first | 
| + // timestamp yet; this can occur if the video starts before the audio. | 
| CHECK_NE(first_packet_timestamp_, kNoTimestamp); | 
| CHECK_GE(first_packet_timestamp_, base::TimeDelta()); | 
| const base::TimeDelta play_delay = |