Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2016 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. | |
| 4 | |
| 5 #include "media/base/silent_sink_suspender.h" | |
| 6 | |
| 7 #include "base/single_thread_task_runner.h" | |
| 8 #include "base/threading/thread_task_runner_handle.h" | |
| 9 | |
| 10 namespace media { | |
| 11 | |
| 12 SilentSinkSuspender::SilentSinkSuspender( | |
| 13 AudioRendererSink::RenderCallback* callback, | |
| 14 base::TimeDelta silence_timeout, | |
| 15 const AudioParameters& params, | |
| 16 const scoped_refptr<AudioRendererSink>& sink, | |
| 17 const scoped_refptr<base::SingleThreadTaskRunner>& worker) | |
| 18 : callback_(callback), | |
| 19 params_(params), | |
| 20 sink_(sink), | |
| 21 task_runner_(base::ThreadTaskRunnerHandle::Get()), | |
| 22 silence_timeout_(silence_timeout), | |
| 23 fake_sink_(worker, params_), | |
| 24 sink_transition_callback_( | |
| 25 base::Bind(&SilentSinkSuspender::TransitionSinks, | |
| 26 base::Unretained(this))) { | |
| 27 DCHECK(params_.IsValid()); | |
| 28 DCHECK(sink_); | |
| 29 DCHECK(callback_); | |
| 30 DCHECK(task_runner_->BelongsToCurrentThread()); | |
| 31 } | |
| 32 | |
| 33 SilentSinkSuspender::~SilentSinkSuspender() { | |
| 34 DCHECK(task_runner_->BelongsToCurrentThread()); | |
| 35 fake_sink_.Stop(); | |
| 36 } | |
| 37 | |
| 38 int SilentSinkSuspender::Render(AudioBus* dest, | |
| 39 uint32_t frames_delayed, | |
| 40 uint32_t frames_skipped) { | |
| 41 // Lock required since AudioRendererSink::Pause() is not synchronous, we need | |
| 42 // to discard these calls during the transition to the fake sink. | |
| 43 base::AutoLock al(transition_lock_); | |
|
o1ka
2016/09/26 12:21:55
It would be good to mention in the header that tra
DaleCurtis
2016/09/26 19:45:58
Done.
| |
| 44 if (is_using_fake_sink_ && dest) { | |
| 45 // Audio should be silent at this point, if not, it will be handled once the | |
| 46 // transition to the fake sink is complete. | |
| 47 dest->Zero(); | |
| 48 return dest->frames(); | |
| 49 } | |
| 50 | |
| 51 // When we're using the |fake_sink_| a null destination will be sent; we store | |
| 52 // the audio data for a future transition out of silence. | |
| 53 if (!dest) { | |
| 54 DCHECK(is_using_fake_sink_); | |
| 55 DCHECK_EQ(frames_delayed, 0u); | |
| 56 DCHECK_EQ(frames_skipped, 0u); | |
| 57 | |
| 58 // If we have no buffers or a transition is pending, one or more extra | |
| 59 // Render() calls have occurred in before TransitionSinks() can run, so we | |
| 60 // store this data for the eventual transition. | |
| 61 if (buffers_after_silence_.empty() || is_transition_pending_) | |
| 62 buffers_after_silence_.push_back(AudioBus::Create(params_)); | |
| 63 dest = buffers_after_silence_.back().get(); | |
| 64 } else if (!buffers_after_silence_.empty()) { | |
| 65 // Drain any non-silent transitional buffers before queuing more audio data. | |
|
o1ka
2016/09/26 12:21:55
Maybe log here?
DaleCurtis
2016/09/26 19:45:58
Don't want any logs on the render thread since the
| |
| 66 // Note: These do not skew clocks derived from frame count since we don't | |
| 67 // issue Render() to the client when returning these buffers. | |
| 68 DCHECK(!is_using_fake_sink_); | |
|
o1ka
2016/09/26 12:21:55
We may want to DCHECK it even if buffers_after_sil
DaleCurtis
2016/09/26 19:45:59
I think the existing pair of DCHECKs covers the ap
| |
| 69 buffers_after_silence_.front()->CopyTo(dest); | |
| 70 buffers_after_silence_.pop_front(); | |
| 71 return dest->frames(); | |
| 72 } | |
| 73 | |
| 74 // Pass-through to client and request rendering. | |
|
o1ka
2016/09/26 12:21:55
Could you add "In case of a fake sink we render to
DaleCurtis
2016/09/26 19:45:59
I think that's covered by the comment in l.65 pret
| |
| 75 callback_->Render(dest, frames_delayed, frames_skipped); | |
| 76 | |
| 77 // Check for silence or real audio data and transition if necessary. | |
| 78 if (!dest->AreFramesZero()) { | |
| 79 first_silence_time_ = base::TimeTicks(); | |
| 80 if (is_using_fake_sink_) { | |
| 81 is_transition_pending_ = true; | |
| 82 task_runner_->PostTask( | |
| 83 FROM_HERE, base::Bind(sink_transition_callback_.callback(), false)); | |
| 84 return dest->frames(); | |
| 85 } | |
| 86 } else if (!is_using_fake_sink_) { | |
| 87 const base::TimeTicks now = base::TimeTicks::Now(); | |
| 88 if (first_silence_time_.is_null()) | |
| 89 first_silence_time_ = now; | |
| 90 if (now - first_silence_time_ > silence_timeout_) { | |
| 91 is_transition_pending_ = true; | |
| 92 task_runner_->PostTask( | |
| 93 FROM_HERE, base::Bind(sink_transition_callback_.callback(), true)); | |
| 94 } | |
| 95 } | |
| 96 | |
| 97 return dest->frames(); | |
| 98 } | |
| 99 | |
| 100 void SilentSinkSuspender::OnRenderError() { | |
| 101 callback_->OnRenderError(); | |
| 102 } | |
| 103 | |
| 104 void SilentSinkSuspender::TransitionSinks(bool use_fake_sink) { | |
|
o1ka
2016/09/26 12:21:55
Maybe some logging? It could help to rule-out this
DaleCurtis
2016/09/26 19:45:59
Was going to add some LOG(INFO) calls, but it's ac
| |
| 105 DCHECK(task_runner_->BelongsToCurrentThread()); | |
| 106 | |
| 107 // Ignore duplicate requests which can occur if the transition takes too long | |
| 108 // and multiple Render() events occur. | |
| 109 if (use_fake_sink && is_using_fake_sink_) | |
| 110 return; | |
| 111 if (!use_fake_sink && !is_using_fake_sink_) | |
| 112 return; | |
|
o1ka
2016/09/26 12:21:55
the two can be (use_fake_sink == is_using_fake_sin
DaleCurtis
2016/09/26 19:45:59
Done.
| |
| 113 | |
| 114 if (use_fake_sink) { | |
| 115 sink_->Pause(); | |
| 116 | |
| 117 // |sink_| may still be executing Render() at this point or even sometime | |
| 118 // after this point, so we must acquire the lock to make sure we don't have | |
| 119 // concurrent Render() execution. Once |is_using_fake_sink_| is set to true, | |
| 120 // calls from |sink_| will be dropped. | |
| 121 { | |
| 122 base::AutoLock al(transition_lock_); | |
| 123 is_transition_pending_ = false; | |
| 124 is_using_fake_sink_ = true; | |
| 125 } | |
| 126 fake_sink_.Start( | |
| 127 base::Bind(base::IgnoreResult(&SilentSinkSuspender::Render), | |
| 128 base::Unretained(this), nullptr, 0, 0)); | |
| 129 } else { | |
| 130 fake_sink_.Stop(); | |
| 131 // Stop() is synchronous for the FakeWorker, so no need to hold a lock here. | |
|
o1ka
2016/09/26 12:21:55
When using a short timeout (for testing, for examp
DaleCurtis
2016/09/26 16:54:27
What do you mean? FakeWorker::Stop() will try to g
DaleCurtis
2016/09/26 19:45:58
Done.
| |
| 132 is_transition_pending_ = false; | |
| 133 is_using_fake_sink_ = false; | |
| 134 sink_->Play(); | |
| 135 } | |
| 136 } | |
| 137 | |
| 138 } // namespace content | |
| OLD | NEW |