| 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_); |
| 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. |
| 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_); |
| 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. |
| 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) { |
| 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 |
| 112 if (use_fake_sink) { |
| 113 sink_->Pause(); |
| 114 |
| 115 // |sink_| may still be executing Render() at this point or even sometime |
| 116 // after this point, so we must acquire the lock to make sure we don't have |
| 117 // concurrent Render() execution. Once |is_using_fake_sink_| is set to true, |
| 118 // calls from |sink_| will be dropped. |
| 119 { |
| 120 base::AutoLock al(transition_lock_); |
| 121 is_transition_pending_ = false; |
| 122 is_using_fake_sink_ = true; |
| 123 } |
| 124 fake_sink_.Start( |
| 125 base::Bind(base::IgnoreResult(&SilentSinkSuspender::Render), |
| 126 base::Unretained(this), nullptr, 0, 0)); |
| 127 } else { |
| 128 fake_sink_.Stop(); |
| 129 |
| 130 // Despite the fake sink having a synchronous Stop(), if this transition |
| 131 // occurs too soon after pausing the real sink, we may have pending Render() |
| 132 // calls from before the transition to the fake sink. As such, we need to |
| 133 // hold the lock here to avoid any races. |
| 134 { |
| 135 base::AutoLock al(transition_lock_); |
| 136 is_transition_pending_ = false; |
| 137 is_using_fake_sink_ = false; |
| 138 } |
| 139 sink_->Play(); |
| 140 } |
| 141 } |
| 142 |
| 143 } // namespace content |
| OLD | NEW |