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

Side by Side Diff: media/audio/linux/alsa_output.cc

Issue 4661001: Simplified AudioOutputStream interface. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: - Created 10 years, 1 month 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 | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2010 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 // THREAD SAFETY 5 // THREAD SAFETY
6 // 6 //
7 // The AlsaPcmOutputStream object's internal state is accessed by two threads: 7 // The AlsaPcmOutputStream object's internal state is accessed by two threads:
8 // 8 //
9 // client thread - creates the object and calls the public APIs. 9 // client thread - creates the object and calls the public APIs.
10 // message loop thread - executes all the internal tasks including querying 10 // message loop thread - executes all the internal tasks including querying
(...skipping 214 matching lines...) Expand 10 before | Expand all | Expand 10 after
225 AudioManagerLinux* manager, 225 AudioManagerLinux* manager,
226 MessageLoop* message_loop) 226 MessageLoop* message_loop)
227 : shared_data_(MessageLoop::current()), 227 : shared_data_(MessageLoop::current()),
228 requested_device_name_(device_name), 228 requested_device_name_(device_name),
229 pcm_format_(alsa_util::BitsToFormat(params.bits_per_sample)), 229 pcm_format_(alsa_util::BitsToFormat(params.bits_per_sample)),
230 channels_(params.channels), 230 channels_(params.channels),
231 sample_rate_(params.sample_rate), 231 sample_rate_(params.sample_rate),
232 bytes_per_sample_(params.bits_per_sample / 8), 232 bytes_per_sample_(params.bits_per_sample / 8),
233 bytes_per_frame_(channels_ * params.bits_per_sample / 8), 233 bytes_per_frame_(channels_ * params.bits_per_sample / 8),
234 should_downmix_(false), 234 should_downmix_(false),
235 latency_micros_(0), 235 packet_size_(params.samples_per_packet * bytes_per_frame_),
236 micros_per_packet_(0), 236 micros_per_packet_(FramesToMicros(
237 params.samples_per_packet, sample_rate_)),
238 latency_micros_(std::max(AlsaPcmOutputStream::kMinLatencyMicros,
239 micros_per_packet_ * 2)),
237 bytes_per_output_frame_(bytes_per_frame_), 240 bytes_per_output_frame_(bytes_per_frame_),
238 alsa_buffer_frames_(0), 241 alsa_buffer_frames_(0),
239 stop_stream_(false), 242 stop_stream_(false),
240 wrapper_(wrapper), 243 wrapper_(wrapper),
241 manager_(manager), 244 manager_(manager),
242 playback_handle_(NULL), 245 playback_handle_(NULL),
243 frames_per_packet_(0), 246 frames_per_packet_(packet_size_ / bytes_per_frame_),
244 client_thread_loop_(MessageLoop::current()), 247 client_thread_loop_(MessageLoop::current()),
245 message_loop_(message_loop) { 248 message_loop_(message_loop) {
246 249
247 // Sanity check input values. 250 // Sanity check input values.
248 if ((params.sample_rate > kAlsaMaxSampleRate) || (params.sample_rate <= 0)) { 251 if ((params.sample_rate > kAlsaMaxSampleRate) || (params.sample_rate <= 0)) {
249 LOG(WARNING) << "Unsupported audio frequency."; 252 LOG(WARNING) << "Unsupported audio frequency.";
250 shared_data_.TransitionTo(kInError); 253 shared_data_.TransitionTo(kInError);
251 } 254 }
252 255
253 if (AudioParameters::AUDIO_PCM_LINEAR != params.format) { 256 if (AudioParameters::AUDIO_PCM_LINEAR != params.format) {
(...skipping 10 matching lines...) Expand all
264 AlsaPcmOutputStream::~AlsaPcmOutputStream() { 267 AlsaPcmOutputStream::~AlsaPcmOutputStream() {
265 InternalState state = shared_data_.state(); 268 InternalState state = shared_data_.state();
266 DCHECK(state == kCreated || state == kIsClosed || state == kInError); 269 DCHECK(state == kCreated || state == kIsClosed || state == kInError);
267 270
268 // TODO(ajwong): Ensure that CloseTask has been called and the 271 // TODO(ajwong): Ensure that CloseTask has been called and the
269 // playback handle released by DCHECKing that playback_handle_ is NULL. 272 // playback handle released by DCHECKing that playback_handle_ is NULL.
270 // Currently, because of Bug 18217, there is a race condition on destruction 273 // Currently, because of Bug 18217, there is a race condition on destruction
271 // where the stream is not always stopped and closed, causing this to fail. 274 // where the stream is not always stopped and closed, causing this to fail.
272 } 275 }
273 276
274 bool AlsaPcmOutputStream::Open(uint32 packet_size) { 277 bool AlsaPcmOutputStream::Open() {
275 DCHECK_EQ(MessageLoop::current(), client_thread_loop_); 278 DCHECK_EQ(MessageLoop::current(), client_thread_loop_);
276 279
277 DCHECK_EQ(0U, packet_size % bytes_per_frame_)
278 << "Buffers should end on a frame boundary. Frame size: "
279 << bytes_per_frame_;
280
281 if (shared_data_.state() == kInError) { 280 if (shared_data_.state() == kInError) {
282 return false; 281 return false;
283 } 282 }
284 283
285 if (!shared_data_.CanTransitionTo(kIsOpened)) { 284 if (!shared_data_.CanTransitionTo(kIsOpened)) {
286 NOTREACHED() << "Invalid state: " << shared_data_.state(); 285 NOTREACHED() << "Invalid state: " << shared_data_.state();
287 return false; 286 return false;
288 } 287 }
289 288
290 // We do not need to check if the transition was successful because 289 // We do not need to check if the transition was successful because
291 // CanTransitionTo() was checked above, and it is assumed that this 290 // CanTransitionTo() was checked above, and it is assumed that this
292 // object's public API is only called on one thread so the state cannot 291 // object's public API is only called on one thread so the state cannot
293 // transition out from under us. 292 // transition out from under us.
294 shared_data_.TransitionTo(kIsOpened); 293 shared_data_.TransitionTo(kIsOpened);
295 message_loop_->PostTask( 294 message_loop_->PostTask(
296 FROM_HERE, 295 FROM_HERE,
297 NewRunnableMethod(this, &AlsaPcmOutputStream::OpenTask, packet_size)); 296 NewRunnableMethod(this, &AlsaPcmOutputStream::OpenTask));
298 297
299 return true; 298 return true;
300 } 299 }
301 300
302 void AlsaPcmOutputStream::Close() { 301 void AlsaPcmOutputStream::Close() {
303 DCHECK_EQ(MessageLoop::current(), client_thread_loop_); 302 DCHECK_EQ(MessageLoop::current(), client_thread_loop_);
304 303
305 // Sanity check that the transition occurs correctly. It is safe to 304 // Sanity check that the transition occurs correctly. It is safe to
306 // continue anyways because all operations for closing are idempotent. 305 // continue anyways because all operations for closing are idempotent.
307 if (shared_data_.TransitionTo(kIsClosed) != kIsClosed) { 306 if (shared_data_.TransitionTo(kIsClosed) != kIsClosed) {
308 NOTREACHED() << "Unable to transition Closed."; 307 NOTREACHED() << "Unable to transition Closed.";
309 } 308 }
310 309
311 // Signal our successful close, and disassociate the source callback.
312 shared_data_.OnClose(this);
313 shared_data_.set_source_callback(NULL);
314
315 message_loop_->PostTask( 310 message_loop_->PostTask(
316 FROM_HERE, 311 FROM_HERE,
317 NewRunnableMethod(this, &AlsaPcmOutputStream::CloseTask)); 312 NewRunnableMethod(this, &AlsaPcmOutputStream::CloseTask));
318 313
319 // Signal to the manager that we're closed and can be removed. Since 314 // Signal to the manager that we're closed and can be removed. Since
320 // we just posted a CloseTask to the message loop, we won't be deleted 315 // we just posted a CloseTask to the message loop, we won't be deleted
321 // immediately, but it will happen soon afterwards. 316 // immediately, but it will happen soon afterwards.
322 manager()->ReleaseOutputStream(this); 317 manager()->ReleaseOutputStream(this);
323 } 318 }
324 319
325 void AlsaPcmOutputStream::Start(AudioSourceCallback* callback) { 320 void AlsaPcmOutputStream::Start(AudioSourceCallback* callback) {
326 DCHECK_EQ(MessageLoop::current(), client_thread_loop_); 321 DCHECK_EQ(MessageLoop::current(), client_thread_loop_);
327 322
328 CHECK(callback); 323 CHECK(callback);
329 324
330 shared_data_.set_source_callback(callback); 325 shared_data_.set_source_callback(callback);
331 326
332 // Only post the task if we can enter the playing state. 327 // Only post the task if we can enter the playing state.
333 if (shared_data_.TransitionTo(kIsPlaying) == kIsPlaying) { 328 if (shared_data_.TransitionTo(kIsPlaying) == kIsPlaying) {
334 message_loop_->PostTask( 329 message_loop_->PostTask(
335 FROM_HERE, 330 FROM_HERE,
336 NewRunnableMethod(this, &AlsaPcmOutputStream::StartTask)); 331 NewRunnableMethod(this, &AlsaPcmOutputStream::StartTask));
337 } 332 }
338 } 333 }
339 334
340 void AlsaPcmOutputStream::Stop() { 335 void AlsaPcmOutputStream::Stop() {
341 DCHECK_EQ(MessageLoop::current(), client_thread_loop_); 336 DCHECK_EQ(MessageLoop::current(), client_thread_loop_);
342 337
338 // Reset the callback, so that it is not called anymore.
339 shared_data_.set_source_callback(NULL);
340
343 shared_data_.TransitionTo(kIsStopped); 341 shared_data_.TransitionTo(kIsStopped);
344 } 342 }
345 343
346 void AlsaPcmOutputStream::SetVolume(double volume) { 344 void AlsaPcmOutputStream::SetVolume(double volume) {
347 DCHECK_EQ(MessageLoop::current(), client_thread_loop_); 345 DCHECK_EQ(MessageLoop::current(), client_thread_loop_);
348 346
349 shared_data_.set_volume(static_cast<float>(volume)); 347 shared_data_.set_volume(static_cast<float>(volume));
350 } 348 }
351 349
352 void AlsaPcmOutputStream::GetVolume(double* volume) { 350 void AlsaPcmOutputStream::GetVolume(double* volume) {
353 DCHECK_EQ(MessageLoop::current(), client_thread_loop_); 351 DCHECK_EQ(MessageLoop::current(), client_thread_loop_);
354 352
355 *volume = shared_data_.volume(); 353 *volume = shared_data_.volume();
356 } 354 }
357 355
358 void AlsaPcmOutputStream::OpenTask(uint32 packet_size) { 356 void AlsaPcmOutputStream::OpenTask() {
359 DCHECK_EQ(message_loop_, MessageLoop::current()); 357 DCHECK_EQ(message_loop_, MessageLoop::current());
360 358
361 // Initialize the configuration variables.
362 packet_size_ = packet_size;
363 frames_per_packet_ = packet_size_ / bytes_per_frame_;
364
365 // Try to open the device. 359 // Try to open the device.
366 micros_per_packet_ =
367 FramesToMicros(packet_size / bytes_per_frame_, sample_rate_);
368 latency_micros_ = std::max(AlsaPcmOutputStream::kMinLatencyMicros,
369 micros_per_packet_ * 2);
370 if (requested_device_name_ == kAutoSelectDevice) { 360 if (requested_device_name_ == kAutoSelectDevice) {
371 playback_handle_ = AutoSelectDevice(latency_micros_); 361 playback_handle_ = AutoSelectDevice(latency_micros_);
372 if (playback_handle_) 362 if (playback_handle_)
373 VLOG(1) << "Auto-selected device: " << device_name_; 363 VLOG(1) << "Auto-selected device: " << device_name_;
374 } else { 364 } else {
375 device_name_ = requested_device_name_; 365 device_name_ = requested_device_name_;
376 playback_handle_ = alsa_util::OpenPlaybackDevice(wrapper_, 366 playback_handle_ = alsa_util::OpenPlaybackDevice(wrapper_,
377 device_name_.c_str(), 367 device_name_.c_str(),
378 channels_, sample_rate_, 368 channels_, sample_rate_,
379 pcm_format_, 369 pcm_format_,
(...skipping 533 matching lines...) Expand 10 before | Expand all | Expand 10 after
913 AudioOutputStream* stream, uint8* dest, uint32 max_size, 903 AudioOutputStream* stream, uint8* dest, uint32 max_size,
914 AudioBuffersState buffers_state) { 904 AudioBuffersState buffers_state) {
915 AutoLock l(lock_); 905 AutoLock l(lock_);
916 if (source_callback_) { 906 if (source_callback_) {
917 return source_callback_->OnMoreData(stream, dest, max_size, buffers_state); 907 return source_callback_->OnMoreData(stream, dest, max_size, buffers_state);
918 } 908 }
919 909
920 return 0; 910 return 0;
921 } 911 }
922 912
923 void AlsaPcmOutputStream::SharedData::OnClose(AudioOutputStream* stream) {
924 AutoLock l(lock_);
925 if (source_callback_) {
926 source_callback_->OnClose(stream);
927 }
928 }
929
930 void AlsaPcmOutputStream::SharedData::OnError(AudioOutputStream* stream, 913 void AlsaPcmOutputStream::SharedData::OnError(AudioOutputStream* stream,
931 int code) { 914 int code) {
932 AutoLock l(lock_); 915 AutoLock l(lock_);
933 if (source_callback_) { 916 if (source_callback_) {
934 source_callback_->OnError(stream, code); 917 source_callback_->OnError(stream, code);
935 } 918 }
936 } 919 }
937 920
938 // Changes the AudioSourceCallback to proxy calls to. Pass in NULL to 921 // Changes the AudioSourceCallback to proxy calls to. Pass in NULL to
939 // release ownership of the currently registered callback. 922 // release ownership of the currently registered callback.
940 void AlsaPcmOutputStream::SharedData::set_source_callback( 923 void AlsaPcmOutputStream::SharedData::set_source_callback(
941 AudioSourceCallback* callback) { 924 AudioSourceCallback* callback) {
942 DCHECK_EQ(MessageLoop::current(), state_transition_loop_); 925 DCHECK_EQ(MessageLoop::current(), state_transition_loop_);
943 AutoLock l(lock_); 926 AutoLock l(lock_);
944 source_callback_ = callback; 927 source_callback_ = callback;
945 } 928 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698