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
3 // found in the LICENSE file.
5 #include "media/renderers/audio_renderer_impl.h"
11 #include "base/bind.h"
12 #include "base/callback.h"
13 #include "base/callback_helpers.h"
14 #include "base/logging.h"
15 #include "base/metrics/histogram.h"
16 #include "base/single_thread_task_runner.h"
17 #include "media/base/audio_buffer.h"
18 #include "media/base/audio_buffer_converter.h"
19 #include "media/base/audio_hardware_config.h"
20 #include "media/base/audio_splicer.h"
21 #include "media/base/bind_to_current_loop.h"
22 #include "media/base/demuxer_stream.h"
23 #include "media/filters/audio_clock.h"
24 #include "media/filters/decrypting_demuxer_stream.h"
30 enum AudioRendererEvent
{
33 RENDER_EVENT_MAX
= RENDER_ERROR
,
36 void HistogramRendererEvent(AudioRendererEvent event
) {
37 UMA_HISTOGRAM_ENUMERATION(
38 "Media.AudioRendererEvents", event
, RENDER_EVENT_MAX
+ 1);
43 AudioRendererImpl::AudioRendererImpl(
44 const scoped_refptr
<base::SingleThreadTaskRunner
>& task_runner
,
45 media::AudioRendererSink
* sink
,
46 ScopedVector
<AudioDecoder
> decoders
,
47 const AudioHardwareConfig
& hardware_config
,
48 const scoped_refptr
<MediaLog
>& media_log
)
49 : task_runner_(task_runner
),
50 expecting_config_changes_(false),
53 new AudioBufferStream(task_runner
, decoders
.Pass(), media_log
)),
54 hardware_config_(hardware_config
),
56 state_(kUninitialized
),
57 buffering_state_(BUFFERING_HAVE_NOTHING
),
61 received_end_of_stream_(false),
62 rendered_end_of_stream_(false),
64 audio_buffer_stream_
->set_splice_observer(base::Bind(
65 &AudioRendererImpl::OnNewSpliceBuffer
, weak_factory_
.GetWeakPtr()));
66 audio_buffer_stream_
->set_config_change_observer(base::Bind(
67 &AudioRendererImpl::OnConfigChange
, weak_factory_
.GetWeakPtr()));
70 AudioRendererImpl::~AudioRendererImpl() {
71 DVLOG(1) << __FUNCTION__
;
72 DCHECK(task_runner_
->BelongsToCurrentThread());
74 // If Render() is in progress, this call will wait for Render() to finish.
75 // After this call, the |sink_| will not call back into |this| anymore.
78 if (!init_cb_
.is_null())
79 base::ResetAndReturn(&init_cb_
).Run(PIPELINE_ERROR_ABORT
);
82 void AudioRendererImpl::StartTicking() {
83 DVLOG(1) << __FUNCTION__
;
84 DCHECK(task_runner_
->BelongsToCurrentThread());
88 base::AutoLock
auto_lock(lock_
);
89 // Wait for an eventual call to SetPlaybackRate() to start rendering.
90 if (playback_rate_
== 0) {
91 DCHECK(!sink_playing_
);
95 StartRendering_Locked();
98 void AudioRendererImpl::StartRendering_Locked() {
99 DVLOG(1) << __FUNCTION__
;
100 DCHECK(task_runner_
->BelongsToCurrentThread());
101 DCHECK_EQ(state_
, kPlaying
);
102 DCHECK(!sink_playing_
);
103 DCHECK_NE(playback_rate_
, 0);
104 lock_
.AssertAcquired();
106 sink_playing_
= true;
108 base::AutoUnlock
auto_unlock(lock_
);
112 void AudioRendererImpl::StopTicking() {
113 DVLOG(1) << __FUNCTION__
;
114 DCHECK(task_runner_
->BelongsToCurrentThread());
118 base::AutoLock
auto_lock(lock_
);
119 // Rendering should have already been stopped with a zero playback rate.
120 if (playback_rate_
== 0) {
121 DCHECK(!sink_playing_
);
125 StopRendering_Locked();
128 void AudioRendererImpl::StopRendering_Locked() {
129 DCHECK(task_runner_
->BelongsToCurrentThread());
130 DCHECK_EQ(state_
, kPlaying
);
131 DCHECK(sink_playing_
);
132 lock_
.AssertAcquired();
134 sink_playing_
= false;
136 base::AutoUnlock
auto_unlock(lock_
);
140 void AudioRendererImpl::SetMediaTime(base::TimeDelta time
) {
141 DVLOG(1) << __FUNCTION__
<< "(" << time
<< ")";
142 DCHECK(task_runner_
->BelongsToCurrentThread());
144 base::AutoLock
auto_lock(lock_
);
146 DCHECK_EQ(state_
, kFlushed
);
148 start_timestamp_
= time
;
149 ended_timestamp_
= kInfiniteDuration();
150 last_render_ticks_
= base::TimeTicks();
151 first_packet_timestamp_
= kNoTimestamp();
152 audio_clock_
.reset(new AudioClock(time
, audio_parameters_
.sample_rate()));
155 base::TimeDelta
AudioRendererImpl::CurrentMediaTime() {
156 // In practice the Render() method is called with a high enough frequency
157 // that returning only the front timestamp is good enough and also prevents
158 // returning values that go backwards in time.
159 base::TimeDelta current_media_time
;
161 base::AutoLock
auto_lock(lock_
);
162 current_media_time
= audio_clock_
->front_timestamp();
165 DVLOG(2) << __FUNCTION__
<< ": " << current_media_time
;
166 return current_media_time
;
169 base::TimeTicks
AudioRendererImpl::GetWallClockTime(base::TimeDelta time
) {
170 base::AutoLock
auto_lock(lock_
);
171 if (last_render_ticks_
.is_null() || playback_rate_
== 0.0)
172 return base::TimeTicks();
174 base::TimeDelta base_time
;
175 if (time
< audio_clock_
->front_timestamp()) {
176 // See notes about |time| values less than |base_time| in TimeSource header.
177 base_time
= audio_clock_
->front_timestamp();
178 } else if (time
> audio_clock_
->back_timestamp()) {
179 base_time
= audio_clock_
->back_timestamp();
181 // No need to estimate time, so return the actual wallclock time.
182 return last_render_ticks_
+ audio_clock_
->TimeUntilPlayback(time
);
185 // In practice, most calls will be estimates given the relatively small window
186 // in which clients can get the actual time.
187 return last_render_ticks_
+ audio_clock_
->TimeUntilPlayback(base_time
) +
188 base::TimeDelta::FromMicroseconds((time
- base_time
).InMicroseconds() /
192 TimeSource
* AudioRendererImpl::GetTimeSource() {
196 void AudioRendererImpl::Flush(const base::Closure
& callback
) {
197 DVLOG(1) << __FUNCTION__
;
198 DCHECK(task_runner_
->BelongsToCurrentThread());
200 base::AutoLock
auto_lock(lock_
);
201 DCHECK_EQ(state_
, kPlaying
);
202 DCHECK(flush_cb_
.is_null());
204 flush_cb_
= callback
;
205 ChangeState_Locked(kFlushing
);
210 ChangeState_Locked(kFlushed
);
214 void AudioRendererImpl::DoFlush_Locked() {
215 DCHECK(task_runner_
->BelongsToCurrentThread());
216 lock_
.AssertAcquired();
218 DCHECK(!pending_read_
);
219 DCHECK_EQ(state_
, kFlushed
);
221 audio_buffer_stream_
->Reset(base::Bind(&AudioRendererImpl::ResetDecoderDone
,
222 weak_factory_
.GetWeakPtr()));
225 void AudioRendererImpl::ResetDecoderDone() {
226 DCHECK(task_runner_
->BelongsToCurrentThread());
228 base::AutoLock
auto_lock(lock_
);
230 DCHECK_EQ(state_
, kFlushed
);
231 DCHECK(!flush_cb_
.is_null());
233 received_end_of_stream_
= false;
234 rendered_end_of_stream_
= false;
236 // Flush() may have been called while underflowed/not fully buffered.
237 if (buffering_state_
!= BUFFERING_HAVE_NOTHING
)
238 SetBufferingState_Locked(BUFFERING_HAVE_NOTHING
);
241 if (buffer_converter_
)
242 buffer_converter_
->Reset();
243 algorithm_
->FlushBuffers();
246 // Changes in buffering state are always posted. Flush callback must only be
247 // run after buffering state has been set back to nothing.
248 task_runner_
->PostTask(FROM_HERE
, base::ResetAndReturn(&flush_cb_
));
251 void AudioRendererImpl::StartPlaying() {
252 DVLOG(1) << __FUNCTION__
;
253 DCHECK(task_runner_
->BelongsToCurrentThread());
255 base::AutoLock
auto_lock(lock_
);
256 DCHECK(!sink_playing_
);
257 DCHECK_EQ(state_
, kFlushed
);
258 DCHECK_EQ(buffering_state_
, BUFFERING_HAVE_NOTHING
);
259 DCHECK(!pending_read_
) << "Pending read must complete before seeking";
261 ChangeState_Locked(kPlaying
);
262 AttemptRead_Locked();
265 void AudioRendererImpl::Initialize(
266 DemuxerStream
* stream
,
267 const PipelineStatusCB
& init_cb
,
268 const SetDecryptorReadyCB
& set_decryptor_ready_cb
,
269 const StatisticsCB
& statistics_cb
,
270 const BufferingStateCB
& buffering_state_cb
,
271 const base::Closure
& ended_cb
,
272 const PipelineStatusCB
& error_cb
,
273 const base::Closure
& waiting_for_decryption_key_cb
) {
274 DVLOG(1) << __FUNCTION__
;
275 DCHECK(task_runner_
->BelongsToCurrentThread());
277 DCHECK_EQ(stream
->type(), DemuxerStream::AUDIO
);
278 DCHECK(!init_cb
.is_null());
279 DCHECK(!statistics_cb
.is_null());
280 DCHECK(!buffering_state_cb
.is_null());
281 DCHECK(!ended_cb
.is_null());
282 DCHECK(!error_cb
.is_null());
283 DCHECK_EQ(kUninitialized
, state_
);
286 state_
= kInitializing
;
288 // Always post |init_cb_| because |this| could be destroyed if initialization
290 init_cb_
= BindToCurrentLoop(init_cb
);
292 buffering_state_cb_
= buffering_state_cb
;
293 ended_cb_
= ended_cb
;
294 error_cb_
= error_cb
;
296 const AudioParameters
& hw_params
= hardware_config_
.GetOutputConfig();
297 expecting_config_changes_
= stream
->SupportsConfigChanges();
298 if (!expecting_config_changes_
|| !hw_params
.IsValid()) {
299 // The actual buffer size is controlled via the size of the AudioBus
300 // provided to Render(), so just choose something reasonable here for looks.
301 int buffer_size
= stream
->audio_decoder_config().samples_per_second() / 100;
302 audio_parameters_
.Reset(
303 AudioParameters::AUDIO_PCM_LOW_LATENCY
,
304 stream
->audio_decoder_config().channel_layout(),
305 ChannelLayoutToChannelCount(
306 stream
->audio_decoder_config().channel_layout()),
307 stream
->audio_decoder_config().samples_per_second(),
308 stream
->audio_decoder_config().bits_per_channel(),
310 buffer_converter_
.reset();
312 audio_parameters_
.Reset(
314 // Always use the source's channel layout and channel count to avoid
315 // premature downmixing (http://crbug.com/379288), platform specific
316 // issues around channel layouts (http://crbug.com/266674), and
317 // unnecessary upmixing overhead.
318 stream
->audio_decoder_config().channel_layout(),
319 ChannelLayoutToChannelCount(
320 stream
->audio_decoder_config().channel_layout()),
321 hw_params
.sample_rate(),
322 hw_params
.bits_per_sample(),
323 hardware_config_
.GetHighLatencyBufferSize());
327 new AudioClock(base::TimeDelta(), audio_parameters_
.sample_rate()));
329 audio_buffer_stream_
->Initialize(
330 stream
, base::Bind(&AudioRendererImpl::OnAudioBufferStreamInitialized
,
331 weak_factory_
.GetWeakPtr()),
332 set_decryptor_ready_cb
, statistics_cb
, waiting_for_decryption_key_cb
);
335 void AudioRendererImpl::OnAudioBufferStreamInitialized(bool success
) {
336 DVLOG(1) << __FUNCTION__
<< ": " << success
;
337 DCHECK(task_runner_
->BelongsToCurrentThread());
339 base::AutoLock
auto_lock(lock_
);
342 state_
= kUninitialized
;
343 base::ResetAndReturn(&init_cb_
).Run(DECODER_ERROR_NOT_SUPPORTED
);
347 if (!audio_parameters_
.IsValid()) {
348 DVLOG(1) << __FUNCTION__
<< ": Invalid audio parameters: "
349 << audio_parameters_
.AsHumanReadableString();
350 ChangeState_Locked(kUninitialized
);
351 base::ResetAndReturn(&init_cb_
).Run(PIPELINE_ERROR_INITIALIZATION_FAILED
);
355 if (expecting_config_changes_
)
356 buffer_converter_
.reset(new AudioBufferConverter(audio_parameters_
));
357 splicer_
.reset(new AudioSplicer(audio_parameters_
.sample_rate()));
359 // We're all good! Continue initializing the rest of the audio renderer
360 // based on the decoder format.
361 algorithm_
.reset(new AudioRendererAlgorithm());
362 algorithm_
->Initialize(audio_parameters_
);
364 ChangeState_Locked(kFlushed
);
366 HistogramRendererEvent(INITIALIZED
);
369 base::AutoUnlock
auto_unlock(lock_
);
370 sink_
->Initialize(audio_parameters_
, this);
373 // Some sinks play on start...
377 DCHECK(!sink_playing_
);
378 base::ResetAndReturn(&init_cb_
).Run(PIPELINE_OK
);
381 void AudioRendererImpl::SetVolume(float volume
) {
382 DCHECK(task_runner_
->BelongsToCurrentThread());
384 sink_
->SetVolume(volume
);
387 void AudioRendererImpl::DecodedAudioReady(
388 AudioBufferStream::Status status
,
389 const scoped_refptr
<AudioBuffer
>& buffer
) {
390 DVLOG(2) << __FUNCTION__
<< "(" << status
<< ")";
391 DCHECK(task_runner_
->BelongsToCurrentThread());
393 base::AutoLock
auto_lock(lock_
);
394 DCHECK(state_
!= kUninitialized
);
396 CHECK(pending_read_
);
397 pending_read_
= false;
399 if (status
== AudioBufferStream::ABORTED
||
400 status
== AudioBufferStream::DEMUXER_READ_ABORTED
) {
401 HandleAbortedReadOrDecodeError(false);
405 if (status
== AudioBufferStream::DECODE_ERROR
) {
406 HandleAbortedReadOrDecodeError(true);
410 DCHECK_EQ(status
, AudioBufferStream::OK
);
411 DCHECK(buffer
.get());
413 if (state_
== kFlushing
) {
414 ChangeState_Locked(kFlushed
);
419 if (expecting_config_changes_
) {
420 DCHECK(buffer_converter_
);
421 buffer_converter_
->AddInput(buffer
);
422 while (buffer_converter_
->HasNextBuffer()) {
423 if (!splicer_
->AddInput(buffer_converter_
->GetNextBuffer())) {
424 HandleAbortedReadOrDecodeError(true);
429 if (!splicer_
->AddInput(buffer
)) {
430 HandleAbortedReadOrDecodeError(true);
435 if (!splicer_
->HasNextBuffer()) {
436 AttemptRead_Locked();
440 bool need_another_buffer
= false;
441 while (splicer_
->HasNextBuffer())
442 need_another_buffer
= HandleSplicerBuffer_Locked(splicer_
->GetNextBuffer());
444 if (!need_another_buffer
&& !CanRead_Locked())
447 AttemptRead_Locked();
450 bool AudioRendererImpl::HandleSplicerBuffer_Locked(
451 const scoped_refptr
<AudioBuffer
>& buffer
) {
452 lock_
.AssertAcquired();
453 if (buffer
->end_of_stream()) {
454 received_end_of_stream_
= true;
456 if (state_
== kPlaying
) {
457 if (IsBeforeStartTime(buffer
))
460 // Trim off any additional time before the start timestamp.
461 const base::TimeDelta trim_time
= start_timestamp_
- buffer
->timestamp();
462 if (trim_time
> base::TimeDelta()) {
463 buffer
->TrimStart(buffer
->frame_count() *
464 (static_cast<double>(trim_time
.InMicroseconds()) /
465 buffer
->duration().InMicroseconds()));
467 // If the entire buffer was trimmed, request a new one.
468 if (!buffer
->frame_count())
472 if (state_
!= kUninitialized
)
473 algorithm_
->EnqueueBuffer(buffer
);
476 // Store the timestamp of the first packet so we know when to start actual
478 if (first_packet_timestamp_
== kNoTimestamp())
479 first_packet_timestamp_
= buffer
->timestamp();
489 DCHECK(!pending_read_
);
493 if (buffer
->end_of_stream() || algorithm_
->IsQueueFull()) {
494 if (buffering_state_
== BUFFERING_HAVE_NOTHING
)
495 SetBufferingState_Locked(BUFFERING_HAVE_ENOUGH
);
503 void AudioRendererImpl::AttemptRead() {
504 base::AutoLock
auto_lock(lock_
);
505 AttemptRead_Locked();
508 void AudioRendererImpl::AttemptRead_Locked() {
509 DCHECK(task_runner_
->BelongsToCurrentThread());
510 lock_
.AssertAcquired();
512 if (!CanRead_Locked())
515 pending_read_
= true;
516 audio_buffer_stream_
->Read(base::Bind(&AudioRendererImpl::DecodedAudioReady
,
517 weak_factory_
.GetWeakPtr()));
520 bool AudioRendererImpl::CanRead_Locked() {
521 lock_
.AssertAcquired();
534 return !pending_read_
&& !received_end_of_stream_
&&
535 !algorithm_
->IsQueueFull();
538 void AudioRendererImpl::SetPlaybackRate(float playback_rate
) {
539 DVLOG(1) << __FUNCTION__
<< "(" << playback_rate
<< ")";
540 DCHECK(task_runner_
->BelongsToCurrentThread());
541 DCHECK_GE(playback_rate
, 0);
544 base::AutoLock
auto_lock(lock_
);
546 // We have two cases here:
547 // Play: current_playback_rate == 0 && playback_rate != 0
548 // Pause: current_playback_rate != 0 && playback_rate == 0
549 float current_playback_rate
= playback_rate_
;
550 playback_rate_
= playback_rate
;
555 if (current_playback_rate
== 0 && playback_rate
!= 0) {
556 StartRendering_Locked();
560 if (current_playback_rate
!= 0 && playback_rate
== 0) {
561 StopRendering_Locked();
566 bool AudioRendererImpl::IsBeforeStartTime(
567 const scoped_refptr
<AudioBuffer
>& buffer
) {
568 DCHECK_EQ(state_
, kPlaying
);
569 return buffer
.get() && !buffer
->end_of_stream() &&
570 (buffer
->timestamp() + buffer
->duration()) < start_timestamp_
;
573 int AudioRendererImpl::Render(AudioBus
* audio_bus
,
574 int audio_delay_milliseconds
) {
575 const int requested_frames
= audio_bus
->frames();
576 base::TimeDelta playback_delay
= base::TimeDelta::FromMilliseconds(
577 audio_delay_milliseconds
);
578 const int delay_frames
= static_cast<int>(playback_delay
.InSecondsF() *
579 audio_parameters_
.sample_rate());
580 int frames_written
= 0;
582 base::AutoLock
auto_lock(lock_
);
583 last_render_ticks_
= base::TimeTicks::Now();
585 // Ensure Stop() hasn't destroyed our |algorithm_| on the pipeline thread.
587 audio_clock_
->WroteAudio(
588 0, requested_frames
, delay_frames
, playback_rate_
);
592 if (playback_rate_
== 0) {
593 audio_clock_
->WroteAudio(
594 0, requested_frames
, delay_frames
, playback_rate_
);
598 // Mute audio by returning 0 when not playing.
599 if (state_
!= kPlaying
) {
600 audio_clock_
->WroteAudio(
601 0, requested_frames
, delay_frames
, playback_rate_
);
605 // Delay playback by writing silence if we haven't reached the first
606 // timestamp yet; this can occur if the video starts before the audio.
607 if (algorithm_
->frames_buffered() > 0) {
608 DCHECK(first_packet_timestamp_
!= kNoTimestamp());
609 const base::TimeDelta play_delay
=
610 first_packet_timestamp_
- audio_clock_
->back_timestamp();
611 if (play_delay
> base::TimeDelta()) {
612 DCHECK_EQ(frames_written
, 0);
614 std::min(static_cast<int>(play_delay
.InSecondsF() *
615 audio_parameters_
.sample_rate()),
617 audio_bus
->ZeroFramesPartial(0, frames_written
);
620 // If there's any space left, actually render the audio; this is where the
621 // aural magic happens.
622 if (frames_written
< requested_frames
) {
623 frames_written
+= algorithm_
->FillBuffer(
624 audio_bus
, frames_written
, requested_frames
- frames_written
,
629 // We use the following conditions to determine end of playback:
630 // 1) Algorithm can not fill the audio callback buffer
631 // 2) We received an end of stream buffer
632 // 3) We haven't already signalled that we've ended
633 // 4) We've played all known audio data sent to hardware
635 // We use the following conditions to determine underflow:
636 // 1) Algorithm can not fill the audio callback buffer
637 // 2) We have NOT received an end of stream buffer
638 // 3) We are in the kPlaying state
640 // Otherwise the buffer has data we can send to the device.
642 // Per the TimeSource API the media time should always increase even after
643 // we've rendered all known audio data. Doing so simplifies scenarios where
644 // we have other sources of media data that need to be scheduled after audio
647 // That being said, we don't want to advance time when underflowed as we
648 // know more decoded frames will eventually arrive. If we did, we would
649 // throw things out of sync when said decoded frames arrive.
650 int frames_after_end_of_stream
= 0;
651 if (frames_written
== 0) {
652 if (received_end_of_stream_
) {
653 if (ended_timestamp_
== kInfiniteDuration())
654 ended_timestamp_
= audio_clock_
->back_timestamp();
655 frames_after_end_of_stream
= requested_frames
;
656 } else if (state_
== kPlaying
&&
657 buffering_state_
!= BUFFERING_HAVE_NOTHING
) {
658 algorithm_
->IncreaseQueueCapacity();
659 SetBufferingState_Locked(BUFFERING_HAVE_NOTHING
);
663 audio_clock_
->WroteAudio(frames_written
+ frames_after_end_of_stream
,
668 if (CanRead_Locked()) {
669 task_runner_
->PostTask(FROM_HERE
,
670 base::Bind(&AudioRendererImpl::AttemptRead
,
671 weak_factory_
.GetWeakPtr()));
674 if (audio_clock_
->front_timestamp() >= ended_timestamp_
&&
675 !rendered_end_of_stream_
) {
676 rendered_end_of_stream_
= true;
677 task_runner_
->PostTask(FROM_HERE
, ended_cb_
);
681 DCHECK_LE(frames_written
, requested_frames
);
682 return frames_written
;
685 void AudioRendererImpl::OnRenderError() {
686 // UMA data tells us this happens ~0.01% of the time. Trigger an error instead
687 // of trying to gracefully fall back to a fake sink. It's very likely
688 // OnRenderError() should be removed and the audio stack handle errors without
689 // notifying clients. See http://crbug.com/234708 for details.
690 HistogramRendererEvent(RENDER_ERROR
);
691 // Post to |task_runner_| as this is called on the audio callback thread.
692 task_runner_
->PostTask(FROM_HERE
,
693 base::Bind(error_cb_
, PIPELINE_ERROR_DECODE
));
696 void AudioRendererImpl::HandleAbortedReadOrDecodeError(bool is_decode_error
) {
697 DCHECK(task_runner_
->BelongsToCurrentThread());
698 lock_
.AssertAcquired();
700 PipelineStatus status
= is_decode_error
? PIPELINE_ERROR_DECODE
: PIPELINE_OK
;
707 ChangeState_Locked(kFlushed
);
708 if (status
== PIPELINE_OK
) {
713 error_cb_
.Run(status
);
714 base::ResetAndReturn(&flush_cb_
).Run();
719 if (status
!= PIPELINE_OK
)
720 error_cb_
.Run(status
);
725 void AudioRendererImpl::ChangeState_Locked(State new_state
) {
726 DVLOG(1) << __FUNCTION__
<< " : " << state_
<< " -> " << new_state
;
727 lock_
.AssertAcquired();
731 void AudioRendererImpl::OnNewSpliceBuffer(base::TimeDelta splice_timestamp
) {
732 DCHECK(task_runner_
->BelongsToCurrentThread());
733 splicer_
->SetSpliceTimestamp(splice_timestamp
);
736 void AudioRendererImpl::OnConfigChange() {
737 DCHECK(task_runner_
->BelongsToCurrentThread());
738 DCHECK(expecting_config_changes_
);
739 buffer_converter_
->ResetTimestampState();
740 // Drain flushed buffers from the converter so the AudioSplicer receives all
741 // data ahead of any OnNewSpliceBuffer() calls. Since discontinuities should
742 // only appear after config changes, AddInput() should never fail here.
743 while (buffer_converter_
->HasNextBuffer())
744 CHECK(splicer_
->AddInput(buffer_converter_
->GetNextBuffer()));
747 void AudioRendererImpl::SetBufferingState_Locked(
748 BufferingState buffering_state
) {
749 DVLOG(1) << __FUNCTION__
<< " : " << buffering_state_
<< " -> "
751 DCHECK_NE(buffering_state_
, buffering_state
);
752 lock_
.AssertAcquired();
753 buffering_state_
= buffering_state
;
755 task_runner_
->PostTask(FROM_HERE
,
756 base::Bind(buffering_state_cb_
, buffering_state_
));