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 "content/renderer/media/webaudio_suspender.h" | |
| 6 | |
| 7 #include "base/single_thread_task_runner.h" | |
| 8 #include "base/threading/thread_task_runner_handle.h" | |
| 9 | |
| 10 namespace content { | |
| 11 | |
| 12 WebAudioSuspender::WebAudioSuspender( | |
| 13 media::AudioRendererSink::RenderCallback* callback, | |
| 14 const media::AudioParameters& params, | |
| 15 const scoped_refptr<media::AudioRendererSink>& sink, | |
| 16 const scoped_refptr<base::SingleThreadTaskRunner>& worker) | |
| 17 : callback_(callback), | |
| 18 params_(params), | |
| 19 sink_(sink), | |
| 20 task_runner_(base::ThreadTaskRunnerHandle::Get()), | |
| 21 fake_sink_(worker, params_), | |
| 22 sink_transition_callback_(base::Bind(&WebAudioSuspender::TransitionSinks, | |
| 23 base::Unretained(this))) { | |
| 24 DCHECK(params_.IsValid()); | |
| 25 DCHECK(sink_); | |
| 26 DCHECK(callback_); | |
| 27 DCHECK(task_runner_->BelongsToCurrentThread()); | |
| 28 } | |
| 29 | |
| 30 WebAudioSuspender::~WebAudioSuspender() { | |
| 31 DCHECK(task_runner_->BelongsToCurrentThread()); | |
| 32 fake_sink_.Stop(); | |
| 33 } | |
| 34 | |
| 35 int WebAudioSuspender::Render(media::AudioBus* dest, | |
| 36 uint32_t frames_delayed, | |
| 37 uint32_t frames_skipped) { | |
| 38 // When we're using the |fake_sink_| a null destination will be sent; we store | |
| 39 // the audio data for a future transition out of silence. | |
| 40 if (!dest) { | |
| 41 DCHECK(is_using_fake_sink_); | |
| 42 DCHECK_EQ(frames_delayed, 0u); | |
| 43 DCHECK_EQ(frames_skipped, 0u); | |
| 44 // If we have no buffers or a transition is pending, one or more extra | |
| 45 // Render() calls have occurred in before TransitionSinks() can run, so we | |
| 46 // store this data for the eventual transition. | |
| 47 if (buffers_after_silence_.empty() || is_transition_pending_) | |
| 48 buffers_after_silence_.push_back(media::AudioBus::Create(params_)); | |
| 49 dest = buffers_after_silence_.back().get(); | |
| 50 } | |
| 51 | |
| 52 // Drain any non-silent transitional buffers before queuing more audio data. | |
| 53 // Note: This may cause skew in the WebAudio clock if too many buffers occur. | |
| 54 if (!is_using_fake_sink_ && !buffers_after_silence_.empty()) { | |
| 55 buffers_after_silence_.front()->CopyTo(dest); | |
| 56 buffers_after_silence_.pop_front(); | |
| 57 return dest->frames(); | |
| 58 } | |
| 59 | |
| 60 // Pass-through to client and request rendering. | |
| 61 callback_->Render(dest, frames_delayed, frames_skipped); | |
| 62 | |
| 63 // Check for silence or real audio data and transition if necessary. | |
| 64 if (!dest->AreFramesZero()) { | |
| 65 first_silence_time_ = base::TimeTicks(); | |
| 66 if (is_using_fake_sink_) { | |
| 67 is_transition_pending_ = true; | |
| 68 task_runner_->PostTask( | |
| 69 FROM_HERE, base::Bind(sink_transition_callback_.callback(), false)); | |
| 70 return dest->frames(); | |
| 71 } | |
| 72 } else if (!is_using_fake_sink_) { | |
| 73 const base::TimeTicks now = base::TimeTicks::Now(); | |
| 74 if (first_silence_time_.is_null()) | |
| 75 first_silence_time_ = now; | |
| 76 if (now - first_silence_time_ > silent_timeout_) { | |
|
o1ka
2016/09/23 10:34:35
can be "else if"
DaleCurtis
2016/09/23 20:24:19
Not if silent timeout is 0 or negative (testing).
o1ka
2016/09/26 12:21:55
Acknowledged.
| |
| 77 is_transition_pending_ = true; | |
| 78 task_runner_->PostTask( | |
| 79 FROM_HERE, base::Bind(sink_transition_callback_.callback(), true)); | |
| 80 } | |
| 81 } | |
| 82 | |
| 83 return dest->frames(); | |
| 84 } | |
| 85 | |
| 86 void WebAudioSuspender::OnRenderError() { | |
| 87 callback_->OnRenderError(); | |
| 88 } | |
| 89 | |
| 90 void WebAudioSuspender::TransitionSinks(bool use_fake_sink) { | |
| 91 DCHECK(task_runner_->BelongsToCurrentThread()); | |
| 92 | |
| 93 // Ignore duplicate requests which can occur if the transition takes too long | |
| 94 // and multiple Render() events occur. | |
| 95 if (use_fake_sink && is_using_fake_sink_) | |
| 96 return; | |
| 97 if (!use_fake_sink && !is_using_fake_sink_) | |
| 98 return; | |
| 99 | |
| 100 // It's safe to modify |is_using_fake_sink_| in between Pause() and | |
| 101 // Play() since no sink is actively calling into Render() during that time. | |
|
o1ka
2016/09/23 10:34:35
ARS interface does not guarantee that Pause() is s
DaleCurtis
2016/09/23 20:24:19
Ah thanks for pointing that out. I'd forgotten. Ad
| |
| 102 if (use_fake_sink) { | |
| 103 sink_->Pause(); | |
| 104 is_transition_pending_ = false; | |
| 105 is_using_fake_sink_ = true; | |
|
o1ka
2016/09/23 10:34:34
The above means that these two flags may be concur
DaleCurtis
2016/09/23 20:24:19
Locked!
| |
| 106 fake_sink_.Start(base::Bind(base::IgnoreResult(&WebAudioSuspender::Render), | |
| 107 base::Unretained(this), nullptr, 0, 0)); | |
| 108 } else { | |
| 109 fake_sink_.Stop(); | |
| 110 is_transition_pending_ = false; | |
| 111 is_using_fake_sink_ = false; | |
| 112 sink_->Play(); | |
| 113 } | |
| 114 } | |
| 115 | |
| 116 } // namespace content | |
| OLD | NEW |