Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(268)

Side by Side Diff: content/renderer/pepper/pepper_media_stream_audio_track_host.cc

Issue 414643003: Support configuring the audio buffer duration in the Pepper MediaStream API. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: More review comments Created 6 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "content/renderer/pepper/pepper_media_stream_audio_track_host.h" 5 #include "content/renderer/pepper/pepper_media_stream_audio_track_host.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 8
9 #include "base/bind.h" 9 #include "base/bind.h"
10 #include "base/location.h" 10 #include "base/location.h"
11 #include "base/logging.h" 11 #include "base/logging.h"
12 #include "base/macros.h" 12 #include "base/macros.h"
13 #include "base/message_loop/message_loop_proxy.h" 13 #include "base/message_loop/message_loop_proxy.h"
14 #include "base/numerics/safe_math.h" 14 #include "base/numerics/safe_math.h"
15 #include "ppapi/c/pp_errors.h" 15 #include "ppapi/c/pp_errors.h"
16 #include "ppapi/c/ppb_audio_buffer.h" 16 #include "ppapi/c/ppb_audio_buffer.h"
17 #include "ppapi/host/dispatch_host_message.h" 17 #include "ppapi/host/dispatch_host_message.h"
18 #include "ppapi/host/host_message_context.h" 18 #include "ppapi/host/host_message_context.h"
19 #include "ppapi/proxy/ppapi_messages.h" 19 #include "ppapi/proxy/ppapi_messages.h"
20 #include "ppapi/shared_impl/media_stream_audio_track_shared.h" 20 #include "ppapi/shared_impl/media_stream_audio_track_shared.h"
21 #include "ppapi/shared_impl/media_stream_buffer.h" 21 #include "ppapi/shared_impl/media_stream_buffer.h"
22 22
23 using media::AudioParameters; 23 using media::AudioParameters;
24 using ppapi::host::HostMessageContext; 24 using ppapi::host::HostMessageContext;
25 using ppapi::MediaStreamAudioTrackShared; 25 using ppapi::MediaStreamAudioTrackShared;
26 26
27 namespace { 27 namespace {
28 28
29 // Max audio buffer duration in milliseconds. 29 // Audio buffer durations in milliseconds.
30 const uint32_t kMaxDuration = 10; 30 const uint32_t kMinDuration = 10;
31 const uint32_t kDefaultDuration = 10;
31 32
32 const int32_t kDefaultNumberOfBuffers = 4; 33 const int32_t kDefaultNumberOfBuffers = 4;
33 const int32_t kMaxNumberOfBuffers = 1000; // 10 sec 34 const int32_t kMaxNumberOfBuffers = 1000; // 10 sec
34 35
35 // Returns true if the |sample_rate| is supported in 36 // Returns true if the |sample_rate| is supported in
36 // |PP_AudioBuffer_SampleRate|, otherwise false. 37 // |PP_AudioBuffer_SampleRate|, otherwise false.
37 PP_AudioBuffer_SampleRate GetPPSampleRate(int sample_rate) { 38 PP_AudioBuffer_SampleRate GetPPSampleRate(int sample_rate) {
38 switch (sample_rate) { 39 switch (sample_rate) {
39 case 8000: 40 case 8000:
40 return PP_AUDIOBUFFER_SAMPLERATE_8000; 41 return PP_AUDIOBUFFER_SAMPLERATE_8000;
(...skipping 17 matching lines...) Expand all
58 } 59 }
59 60
60 } // namespace 61 } // namespace
61 62
62 namespace content { 63 namespace content {
63 64
64 PepperMediaStreamAudioTrackHost::AudioSink::AudioSink( 65 PepperMediaStreamAudioTrackHost::AudioSink::AudioSink(
65 PepperMediaStreamAudioTrackHost* host) 66 PepperMediaStreamAudioTrackHost* host)
66 : host_(host), 67 : host_(host),
67 buffer_data_size_(0), 68 buffer_data_size_(0),
69 active_buffer_index_(-1),
70 active_buffer_offset_(0),
71 init_buffers_count_(0),
68 main_message_loop_proxy_(base::MessageLoopProxy::current()), 72 main_message_loop_proxy_(base::MessageLoopProxy::current()),
69 weak_factory_(this), 73 weak_factory_(this),
70 number_of_buffers_(kDefaultNumberOfBuffers), 74 number_of_buffers_(kDefaultNumberOfBuffers),
71 bytes_per_second_(0) {} 75 bytes_per_second_(0),
76 user_buffer_duration_(kDefaultDuration) {}
72 77
73 PepperMediaStreamAudioTrackHost::AudioSink::~AudioSink() { 78 PepperMediaStreamAudioTrackHost::AudioSink::~AudioSink() {
74 DCHECK_EQ(main_message_loop_proxy_, base::MessageLoopProxy::current()); 79 DCHECK_EQ(main_message_loop_proxy_, base::MessageLoopProxy::current());
75 } 80 }
76 81
77 void PepperMediaStreamAudioTrackHost::AudioSink::EnqueueBuffer(int32_t index) { 82 void PepperMediaStreamAudioTrackHost::AudioSink::EnqueueBuffer(int32_t index) {
78 DCHECK_EQ(main_message_loop_proxy_, base::MessageLoopProxy::current()); 83 DCHECK_EQ(main_message_loop_proxy_, base::MessageLoopProxy::current());
79 DCHECK_GE(index, 0); 84 DCHECK_GE(index, 0);
80 DCHECK_LT(index, host_->buffer_manager()->number_of_buffers()); 85 DCHECK_LT(index, host_->buffer_manager()->number_of_buffers());
81 base::AutoLock lock(lock_); 86 base::AutoLock lock(lock_);
82 buffers_.push_back(index); 87 buffers_.push_back(index);
83 } 88 }
84 89
85 void PepperMediaStreamAudioTrackHost::AudioSink::Configure( 90 void PepperMediaStreamAudioTrackHost::AudioSink::Configure(
86 int32_t number_of_buffers) { 91 int32_t number_of_buffers, int32_t duration) {
87 DCHECK_EQ(main_message_loop_proxy_, base::MessageLoopProxy::current()); 92 DCHECK_EQ(main_message_loop_proxy_, base::MessageLoopProxy::current());
88 bool changed = false; 93 bool changed = false;
89 if (number_of_buffers != number_of_buffers_) 94 if (number_of_buffers != number_of_buffers_)
90 changed = true; 95 changed = true;
96 if (duration != 0 && duration != user_buffer_duration_) {
97 user_buffer_duration_ = duration;
98 changed = true;
99 }
91 number_of_buffers_ = number_of_buffers; 100 number_of_buffers_ = number_of_buffers;
92 101
93 // Initialize later in OnSetFormat if bytes_per_second_ is not know yet. 102 // Initialize later in OnSetFormat if bytes_per_second_ is not know yet.
94 if (changed && bytes_per_second_ > 0) 103 if (changed && bytes_per_second_ > 0 && bytes_per_frame_ > 0)
95 InitBuffers(); 104 InitBuffers();
96 } 105 }
97 106
98 void PepperMediaStreamAudioTrackHost::AudioSink::SetFormatOnMainThread( 107 void PepperMediaStreamAudioTrackHost::AudioSink::SetFormatOnMainThread(
99 int bytes_per_second) { 108 int bytes_per_second, int bytes_per_frame) {
100 bytes_per_second_ = bytes_per_second; 109 bytes_per_second_ = bytes_per_second;
110 bytes_per_frame_ = bytes_per_frame;
101 InitBuffers(); 111 InitBuffers();
102 } 112 }
103 113
104 void PepperMediaStreamAudioTrackHost::AudioSink::InitBuffers() { 114 void PepperMediaStreamAudioTrackHost::AudioSink::InitBuffers() {
105 DCHECK_EQ(main_message_loop_proxy_, base::MessageLoopProxy::current()); 115 DCHECK_EQ(main_message_loop_proxy_, base::MessageLoopProxy::current());
116 {
117 base::AutoLock lock(lock_);
118 // Clear |buffers_|, so the audio thread will drop all incoming audio data.
119 buffers_.clear();
120 init_buffers_count_++;
121 }
122 int32_t frame_rate = bytes_per_second_ / bytes_per_frame_;
123 base::CheckedNumeric<int32_t> frames_per_buffer = user_buffer_duration_;
124 frames_per_buffer *= frame_rate;
125 frames_per_buffer /= base::Time::kMillisecondsPerSecond;
126 base::CheckedNumeric<int32_t> buffer_audio_size =
127 frames_per_buffer * bytes_per_frame_;
106 // The size is slightly bigger than necessary, because 8 extra bytes are 128 // The size is slightly bigger than necessary, because 8 extra bytes are
107 // added into the struct. Also see |MediaStreamBuffer|. 129 // added into the struct. Also see |MediaStreamBuffer|. Also, the size of the
108 base::CheckedNumeric<int32_t> buffer_size = bytes_per_second_; 130 // buffer may be larger than requested, since the size of each buffer will be
109 buffer_size *= kMaxDuration; 131 // 4-byte aligned.
110 buffer_size /= base::Time::kMillisecondsPerSecond; 132 base::CheckedNumeric<int32_t> buffer_size = buffer_audio_size;
111 buffer_size += sizeof(ppapi::MediaStreamBuffer::Audio); 133 buffer_size += sizeof(ppapi::MediaStreamBuffer::Audio);
134 DCHECK_GT(buffer_size.ValueOrDie(), 0);
dmichael (off chromium) 2014/07/29 16:41:24 Can you use IsValid() somewhere before ValidOrDie?
Anand Mistry (off Chromium) 2014/07/30 00:22:05 The only number that comes from the plugin, and is
dmichael (off chromium) 2014/07/30 23:09:22 Okay, thanks for explaining. Fine to do in a follo
135
136 // We don't need hold |lock_| during |host->InitBuffers()| call, because
137 // we just cleared |buffers_| , so the audio thread will drop all incoming
138 // audio data, and not use buffers in |host_|.
112 bool result = host_->InitBuffers(number_of_buffers_, 139 bool result = host_->InitBuffers(number_of_buffers_,
113 buffer_size.ValueOrDie(), 140 buffer_size.ValueOrDie(),
114 kRead); 141 kRead);
115 // TODO(penghuang): Send PP_ERROR_NOMEMORY to plugin. 142 // TODO(penghuang): Send PP_ERROR_NOMEMORY to plugin.
116 CHECK(result); 143 CHECK(result);
144
145 // Fill the |buffers_|, so the audio thread can continue receiving audio data.
117 base::AutoLock lock(lock_); 146 base::AutoLock lock(lock_);
118 buffers_.clear(); 147 output_buffer_size_ = buffer_audio_size.ValueOrDie();
119 for (int32_t i = 0; i < number_of_buffers_; ++i) { 148 for (int32_t i = 0; i < number_of_buffers_; ++i) {
120 int32_t index = host_->buffer_manager()->DequeueBuffer(); 149 int32_t index = host_->buffer_manager()->DequeueBuffer();
121 DCHECK_GE(index, 0); 150 DCHECK_GE(index, 0);
122 buffers_.push_back(index); 151 buffers_.push_back(index);
123 } 152 }
124 } 153 }
125 154
126 void PepperMediaStreamAudioTrackHost::AudioSink:: 155 void PepperMediaStreamAudioTrackHost::AudioSink::
127 SendEnqueueBufferMessageOnMainThread(int32_t index) { 156 SendEnqueueBufferMessageOnMainThread(int32_t index,
157 int32_t init_buffers_count) {
128 DCHECK_EQ(main_message_loop_proxy_, base::MessageLoopProxy::current()); 158 DCHECK_EQ(main_message_loop_proxy_, base::MessageLoopProxy::current());
129 host_->SendEnqueueBufferMessageToPlugin(index); 159 // If |InitBuffers()| is called after this task being posted from the audio
160 // thread, the buffer should become invalid already. We should ignore it. And
161 // because only the main thread modifies the |init_buffers_count_|, so we
162 // don't need lock |lock_| here (main thread).
163 if (init_buffers_count == init_buffers_count_)
164 host_->SendEnqueueBufferMessageToPlugin(index);
130 } 165 }
131 166
132 void PepperMediaStreamAudioTrackHost::AudioSink::OnData(const int16* audio_data, 167 void PepperMediaStreamAudioTrackHost::AudioSink::OnData(const int16* audio_data,
133 int sample_rate, 168 int sample_rate,
134 int number_of_channels, 169 int number_of_channels,
135 int number_of_frames) { 170 int number_of_frames) {
136 DCHECK(audio_thread_checker_.CalledOnValidThread()); 171 DCHECK(audio_thread_checker_.CalledOnValidThread());
137 DCHECK(audio_data); 172 DCHECK(audio_data);
138 DCHECK_EQ(sample_rate, audio_params_.sample_rate()); 173 DCHECK_EQ(sample_rate, audio_params_.sample_rate());
139 DCHECK_EQ(number_of_channels, audio_params_.channels()); 174 DCHECK_EQ(number_of_channels, audio_params_.channels());
140 DCHECK_EQ(number_of_frames, audio_params_.frames_per_buffer()); 175 DCHECK_EQ(number_of_frames, audio_params_.frames_per_buffer());
141 int32_t index = -1; 176
142 { 177 const uint32_t bytes_per_frame = number_of_channels *
143 base::AutoLock lock(lock_); 178 audio_params_.bits_per_sample() / 8;
144 if (!buffers_.empty()) { 179
145 index = buffers_.front(); 180 int frames_remaining = number_of_frames;
181 base::TimeDelta timestamp_offset;
182
183 base::AutoLock lock(lock_);
184 while (frames_remaining) {
185 if (active_init_buffers_count_ != init_buffers_count_) {
186 // Buffers have changed, so drop the active buffer.
187 active_buffer_index_ = -1;
188 }
189 if (active_buffer_index_ == -1 && !buffers_.empty()) {
190 active_init_buffers_count_ = init_buffers_count_;
191 active_buffer_offset_ = 0;
192 active_buffer_index_ = buffers_.front();
146 buffers_.pop_front(); 193 buffers_.pop_front();
147 } 194 }
148 } 195 if (active_buffer_index_ == -1) {
196 // Eek! We're dropping frames. Bad, bad, bad!
197 break;
198 }
149 199
150 if (index != -1) {
151 // TODO(penghuang): support re-sampling, etc. 200 // TODO(penghuang): support re-sampling, etc.
152 ppapi::MediaStreamBuffer::Audio* buffer = 201 ppapi::MediaStreamBuffer::Audio* buffer =
153 &(host_->buffer_manager()->GetBufferPointer(index)->audio); 202 &(host_->buffer_manager()->GetBufferPointer(active_buffer_index_)
154 buffer->header.size = host_->buffer_manager()->buffer_size(); 203 ->audio);
155 buffer->header.type = ppapi::MediaStreamBuffer::TYPE_AUDIO; 204 if (active_buffer_offset_ == 0) {
156 buffer->timestamp = timestamp_.InMillisecondsF(); 205 // The active buffer is new, so initialise the header and metadata fields.
157 buffer->sample_rate = static_cast<PP_AudioBuffer_SampleRate>(sample_rate); 206 buffer->header.size = host_->buffer_manager()->buffer_size();
158 buffer->number_of_channels = number_of_channels; 207 buffer->header.type = ppapi::MediaStreamBuffer::TYPE_AUDIO;
159 buffer->number_of_samples = number_of_channels * number_of_frames; 208 buffer->timestamp = (timestamp_ + timestamp_offset).InMillisecondsF();
160 buffer->data_size = buffer_data_size_; 209 buffer->sample_rate = static_cast<PP_AudioBuffer_SampleRate>(sample_rate);
161 memcpy(buffer->data, audio_data, buffer_data_size_); 210 buffer->data_size = output_buffer_size_;
211 buffer->number_of_channels = number_of_channels;
212 buffer->number_of_samples = buffer->data_size / bytes_per_frame;
213 }
214 uint32_t buffer_bytes_remaining =
215 buffer->data_size - active_buffer_offset_;
216 DCHECK_EQ(buffer_bytes_remaining % bytes_per_frame, 0U);
217 uint32_t incoming_bytes_remaining = frames_remaining * bytes_per_frame;
218 uint32_t bytes_to_copy = std::min(buffer_bytes_remaining,
219 incoming_bytes_remaining);
220 uint32_t frames_to_copy = bytes_to_copy / bytes_per_frame;
221 DCHECK_EQ(bytes_to_copy % bytes_per_frame, 0U);
222 memcpy(buffer->data + active_buffer_offset_,
223 audio_data, bytes_to_copy);
224 active_buffer_offset_ += bytes_to_copy;
225 audio_data += bytes_to_copy / sizeof(*audio_data);
226 frames_remaining -= frames_to_copy;
227 timestamp_offset += base::TimeDelta::FromMilliseconds(
228 frames_to_copy * base::Time::kMillisecondsPerSecond / sample_rate);
162 229
163 main_message_loop_proxy_->PostTask( 230 DCHECK_LE(active_buffer_offset_, buffer->data_size);
164 FROM_HERE, 231 if (active_buffer_offset_ == buffer->data_size) {
165 base::Bind(&AudioSink::SendEnqueueBufferMessageOnMainThread, 232 main_message_loop_proxy_->PostTask(
166 weak_factory_.GetWeakPtr(), 233 FROM_HERE,
167 index)); 234 base::Bind(&AudioSink::SendEnqueueBufferMessageOnMainThread,
235 weak_factory_.GetWeakPtr(),
236 active_buffer_index_,
237 init_buffers_count_));
238 active_buffer_index_ = -1;
239 }
168 } 240 }
169 timestamp_ += buffer_duration_; 241 timestamp_ += buffer_duration_;
170 } 242 }
171 243
172 void PepperMediaStreamAudioTrackHost::AudioSink::OnSetFormat( 244 void PepperMediaStreamAudioTrackHost::AudioSink::OnSetFormat(
173 const AudioParameters& params) { 245 const AudioParameters& params) {
174 DCHECK(params.IsValid()); 246 DCHECK(params.IsValid());
175 DCHECK_LE(params.GetBufferDuration().InMilliseconds(), kMaxDuration); 247 // TODO(amistry): How do you handle the case where the user configures a
248 // duration that's shorter than the received buffer duration? One option is to
249 // double buffer, where the size of the intermediate ring buffer is at least
250 // max(user requested duration, received buffer duration). There are other
251 // ways of dealing with it, but which one is "correct"?
252 DCHECK_LE(params.GetBufferDuration().InMilliseconds(), kMinDuration);
176 DCHECK_EQ(params.bits_per_sample(), 16); 253 DCHECK_EQ(params.bits_per_sample(), 16);
177 DCHECK_NE(GetPPSampleRate(params.sample_rate()), 254 DCHECK_NE(GetPPSampleRate(params.sample_rate()),
178 PP_AUDIOBUFFER_SAMPLERATE_UNKNOWN); 255 PP_AUDIOBUFFER_SAMPLERATE_UNKNOWN);
179 256
180 audio_params_ = params; 257 audio_params_ = params;
181 258
182 // TODO(penghuang): support setting format more than once. 259 // TODO(penghuang): support setting format more than once.
183 buffer_duration_ = params.GetBufferDuration(); 260 buffer_duration_ = params.GetBufferDuration();
184 buffer_data_size_ = params.GetBytesPerBuffer(); 261 buffer_data_size_ = params.GetBytesPerBuffer();
185 262
186 if (original_audio_params_.IsValid()) { 263 if (original_audio_params_.IsValid()) {
187 DCHECK_EQ(params.sample_rate(), original_audio_params_.sample_rate()); 264 DCHECK_EQ(params.sample_rate(), original_audio_params_.sample_rate());
188 DCHECK_EQ(params.bits_per_sample(), 265 DCHECK_EQ(params.bits_per_sample(),
189 original_audio_params_.bits_per_sample()); 266 original_audio_params_.bits_per_sample());
190 DCHECK_EQ(params.channels(), original_audio_params_.channels()); 267 DCHECK_EQ(params.channels(), original_audio_params_.channels());
191 } else { 268 } else {
192 audio_thread_checker_.DetachFromThread(); 269 audio_thread_checker_.DetachFromThread();
193 original_audio_params_ = params; 270 original_audio_params_ = params;
194 271
272 int bytes_per_frame = params.channels() * params.bits_per_sample() / 8;
195 main_message_loop_proxy_->PostTask( 273 main_message_loop_proxy_->PostTask(
196 FROM_HERE, 274 FROM_HERE,
197 base::Bind(&AudioSink::SetFormatOnMainThread, 275 base::Bind(&AudioSink::SetFormatOnMainThread,
198 weak_factory_.GetWeakPtr(), 276 weak_factory_.GetWeakPtr(),
199 params.GetBytesPerSecond())); 277 params.GetBytesPerSecond(),
278 bytes_per_frame));
200 } 279 }
201 } 280 }
202 281
203 PepperMediaStreamAudioTrackHost::PepperMediaStreamAudioTrackHost( 282 PepperMediaStreamAudioTrackHost::PepperMediaStreamAudioTrackHost(
204 RendererPpapiHost* host, 283 RendererPpapiHost* host,
205 PP_Instance instance, 284 PP_Instance instance,
206 PP_Resource resource, 285 PP_Resource resource,
207 const blink::WebMediaStreamTrack& track) 286 const blink::WebMediaStreamTrack& track)
208 : PepperMediaStreamTrackHostBase(host, instance, resource), 287 : PepperMediaStreamTrackHostBase(host, instance, resource),
209 track_(track), 288 track_(track),
(...skipping 19 matching lines...) Expand all
229 308
230 int32_t PepperMediaStreamAudioTrackHost::OnHostMsgConfigure( 309 int32_t PepperMediaStreamAudioTrackHost::OnHostMsgConfigure(
231 HostMessageContext* context, 310 HostMessageContext* context,
232 const MediaStreamAudioTrackShared::Attributes& attributes) { 311 const MediaStreamAudioTrackShared::Attributes& attributes) {
233 if (!MediaStreamAudioTrackShared::VerifyAttributes(attributes)) 312 if (!MediaStreamAudioTrackShared::VerifyAttributes(attributes))
234 return PP_ERROR_BADARGUMENT; 313 return PP_ERROR_BADARGUMENT;
235 314
236 int32_t buffers = attributes.buffers 315 int32_t buffers = attributes.buffers
237 ? std::min(kMaxNumberOfBuffers, attributes.buffers) 316 ? std::min(kMaxNumberOfBuffers, attributes.buffers)
238 : kDefaultNumberOfBuffers; 317 : kDefaultNumberOfBuffers;
239 audio_sink_.Configure(buffers); 318 audio_sink_.Configure(buffers, attributes.duration);
240 319
241 context->reply_msg = PpapiPluginMsg_MediaStreamAudioTrack_ConfigureReply(); 320 context->reply_msg = PpapiPluginMsg_MediaStreamAudioTrack_ConfigureReply();
242 return PP_OK; 321 return PP_OK;
243 } 322 }
244 323
245 void PepperMediaStreamAudioTrackHost::OnClose() { 324 void PepperMediaStreamAudioTrackHost::OnClose() {
246 if (connected_) { 325 if (connected_) {
247 MediaStreamAudioSink::RemoveFromAudioTrack(&audio_sink_, track_); 326 MediaStreamAudioSink::RemoveFromAudioTrack(&audio_sink_, track_);
248 connected_ = false; 327 connected_ = false;
249 } 328 }
250 } 329 }
251 330
252 void PepperMediaStreamAudioTrackHost::OnNewBufferEnqueued() { 331 void PepperMediaStreamAudioTrackHost::OnNewBufferEnqueued() {
253 int32_t index = buffer_manager()->DequeueBuffer(); 332 int32_t index = buffer_manager()->DequeueBuffer();
254 DCHECK_GE(index, 0); 333 DCHECK_GE(index, 0);
255 audio_sink_.EnqueueBuffer(index); 334 audio_sink_.EnqueueBuffer(index);
256 } 335 }
257 336
258 void PepperMediaStreamAudioTrackHost::DidConnectPendingHostToResource() { 337 void PepperMediaStreamAudioTrackHost::DidConnectPendingHostToResource() {
259 if (!connected_) { 338 if (!connected_) {
260 MediaStreamAudioSink::AddToAudioTrack(&audio_sink_, track_); 339 MediaStreamAudioSink::AddToAudioTrack(&audio_sink_, track_);
261 connected_ = true; 340 connected_ = true;
262 } 341 }
263 } 342 }
264 343
265 } // namespace content 344 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698