• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2013 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/webaudiosourceprovider_impl.h"
6 
7 #include <vector>
8 
9 #include "base/bind.h"
10 #include "base/callback_helpers.h"
11 #include "base/logging.h"
12 #include "media/base/bind_to_current_loop.h"
13 #include "third_party/WebKit/public/platform/WebAudioSourceProviderClient.h"
14 
15 using blink::WebVector;
16 
17 namespace content {
18 
19 namespace {
20 
21 // Simple helper class for Try() locks.  Lock is Try()'d on construction and
22 // must be checked via the locked() attribute.  If acquisition was successful
23 // the lock will be released upon destruction.
24 // TODO(dalecurtis): This should probably move to base/ if others start using
25 // this pattern.
26 class AutoTryLock {
27  public:
AutoTryLock(base::Lock & lock)28   explicit AutoTryLock(base::Lock& lock)
29       : lock_(lock),
30         acquired_(lock_.Try()) {}
31 
locked() const32   bool locked() const { return acquired_; }
33 
~AutoTryLock()34   ~AutoTryLock() {
35     if (acquired_) {
36       lock_.AssertAcquired();
37       lock_.Release();
38     }
39   }
40 
41  private:
42   base::Lock& lock_;
43   const bool acquired_;
44   DISALLOW_COPY_AND_ASSIGN(AutoTryLock);
45 };
46 
47 }  // namespace
48 
WebAudioSourceProviderImpl(const scoped_refptr<media::AudioRendererSink> & sink)49 WebAudioSourceProviderImpl::WebAudioSourceProviderImpl(
50     const scoped_refptr<media::AudioRendererSink>& sink)
51     : channels_(0),
52       sample_rate_(0),
53       volume_(1.0),
54       state_(kStopped),
55       renderer_(NULL),
56       client_(NULL),
57       sink_(sink),
58       weak_factory_(this) {}
59 
~WebAudioSourceProviderImpl()60 WebAudioSourceProviderImpl::~WebAudioSourceProviderImpl() {
61 }
62 
setClient(blink::WebAudioSourceProviderClient * client)63 void WebAudioSourceProviderImpl::setClient(
64     blink::WebAudioSourceProviderClient* client) {
65   base::AutoLock auto_lock(sink_lock_);
66   if (client && client != client_) {
67     // Detach the audio renderer from normal playback.
68     sink_->Stop();
69 
70     // The client will now take control by calling provideInput() periodically.
71     client_ = client;
72 
73     set_format_cb_ = media::BindToCurrentLoop(base::Bind(
74         &WebAudioSourceProviderImpl::OnSetFormat, weak_factory_.GetWeakPtr()));
75 
76     // If |renderer_| is set, then run |set_format_cb_| to send |client_|
77     // the current format info. If |renderer_| is not set, then |set_format_cb_|
78     // will get called when Initialize() is called.
79     // Note: Always using |set_format_cb_| ensures we have the same
80     // locking order when calling into |client_|.
81     if (renderer_)
82       base::ResetAndReturn(&set_format_cb_).Run();
83   } else if (!client && client_) {
84     // Restore normal playback.
85     client_ = NULL;
86     sink_->SetVolume(volume_);
87     if (state_ >= kStarted)
88       sink_->Start();
89     if (state_ >= kPlaying)
90       sink_->Play();
91   }
92 }
93 
provideInput(const WebVector<float * > & audio_data,size_t number_of_frames)94 void WebAudioSourceProviderImpl::provideInput(
95     const WebVector<float*>& audio_data, size_t number_of_frames) {
96   if (!bus_wrapper_ ||
97       static_cast<size_t>(bus_wrapper_->channels()) != audio_data.size()) {
98     bus_wrapper_ = media::AudioBus::CreateWrapper(audio_data.size());
99   }
100 
101   bus_wrapper_->set_frames(number_of_frames);
102   for (size_t i = 0; i < audio_data.size(); ++i)
103     bus_wrapper_->SetChannelData(i, audio_data[i]);
104 
105   // Use a try lock to avoid contention in the real-time audio thread.
106   AutoTryLock auto_try_lock(sink_lock_);
107   if (!auto_try_lock.locked() || state_ != kPlaying) {
108     // Provide silence if we failed to acquire the lock or the source is not
109     // running.
110     bus_wrapper_->Zero();
111     return;
112   }
113 
114   DCHECK(renderer_);
115   DCHECK(client_);
116   DCHECK_EQ(channels_, bus_wrapper_->channels());
117   const size_t frames = renderer_->Render(bus_wrapper_.get(), 0);
118   if (frames < number_of_frames)
119     bus_wrapper_->ZeroFramesPartial(frames, number_of_frames - frames);
120   bus_wrapper_->Scale(volume_);
121 }
122 
Start()123 void WebAudioSourceProviderImpl::Start() {
124   base::AutoLock auto_lock(sink_lock_);
125   DCHECK_EQ(state_, kStopped);
126   state_ = kStarted;
127   if (!client_)
128     sink_->Start();
129 }
130 
Stop()131 void WebAudioSourceProviderImpl::Stop() {
132   base::AutoLock auto_lock(sink_lock_);
133   state_ = kStopped;
134   if (!client_)
135     sink_->Stop();
136 }
137 
Play()138 void WebAudioSourceProviderImpl::Play() {
139   base::AutoLock auto_lock(sink_lock_);
140   DCHECK_EQ(state_, kStarted);
141   state_ = kPlaying;
142   if (!client_)
143     sink_->Play();
144 }
145 
Pause()146 void WebAudioSourceProviderImpl::Pause() {
147   base::AutoLock auto_lock(sink_lock_);
148   DCHECK(state_ == kPlaying || state_ == kStarted);
149   state_ = kStarted;
150   if (!client_)
151     sink_->Pause();
152 }
153 
SetVolume(double volume)154 bool WebAudioSourceProviderImpl::SetVolume(double volume) {
155   base::AutoLock auto_lock(sink_lock_);
156   volume_ = volume;
157   if (!client_)
158     sink_->SetVolume(volume);
159   return true;
160 }
161 
Initialize(const media::AudioParameters & params,RenderCallback * renderer)162 void WebAudioSourceProviderImpl::Initialize(
163     const media::AudioParameters& params,
164     RenderCallback* renderer) {
165   base::AutoLock auto_lock(sink_lock_);
166   CHECK(!renderer_);
167   renderer_ = renderer;
168 
169   DCHECK_EQ(state_, kStopped);
170   sink_->Initialize(params, renderer);
171 
172   // Keep track of the format in case the client hasn't yet been set.
173   channels_ = params.channels();
174   sample_rate_ = params.sample_rate();
175 
176   if (!set_format_cb_.is_null())
177     base::ResetAndReturn(&set_format_cb_).Run();
178 }
179 
OnSetFormat()180 void WebAudioSourceProviderImpl::OnSetFormat() {
181   base::AutoLock auto_lock(sink_lock_);
182   if (!client_)
183     return;
184 
185   // Inform Blink about the audio stream format.
186   client_->setFormat(channels_, sample_rate_);
187 }
188 
189 }  // namespace content
190