OLD | NEW |
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2009 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 // TODO(hclam): Several changes need to be made to this code: | 5 // TODO(hclam): Several changes need to be made to this code: |
6 // 1. We should host AudioRendererHost on a dedicated audio thread. Doing | 6 // 1. We should host AudioRendererHost on a dedicated audio thread. Doing |
7 // so we don't have to worry about blocking method calls such as | 7 // so we don't have to worry about blocking method calls such as |
8 // play / stop an audio stream. | 8 // play / stop an audio stream. |
9 // 2. Move locked data structures into a separate structure that sanity | 9 // 2. Move locked data structures into a separate structure that sanity |
10 // checks access by different threads that use it. | 10 // checks access by different threads that use it. |
11 // | 11 // |
12 // SEMANTICS OF |state_| | 12 // SEMANTICS OF |state_| |
13 // Note that |state_| of IPCAudioSource is accessed on two thread. Namely | 13 // Note that |state_| of IPCAudioSource is accessed on two thread. Namely |
14 // the IO thread and the audio thread. IO thread is the thread on which | 14 // the IO thread and the audio thread. IO thread is the thread on which |
15 // IPAudioSource::Play(), IPCAudioSource::Pause() are called. Audio thread | 15 // IPAudioSource::Play(), IPCAudioSource::Pause() are called. Audio thread |
16 // is a thread operated by the audio hardware for requesting data. | 16 // is a thread operated by the audio hardware for requesting data. |
17 // It is important that |state_| is only written on the IO thread because | 17 // It is important that |state_| is only written on the IO thread because |
18 // reading of such state in Play() and Pause() is not protected. However, | 18 // reading of such state in Play() and Pause() is not protected. However, |
19 // since OnMoreData() is called on the audio thread and reads |state_| | 19 // since OnMoreData() is called on the audio thread and reads |state_| |
20 // variable. Writing to this variable needs to be protected in Play() | 20 // variable. Writing to this variable needs to be protected in Play() |
21 // and Pause(). | 21 // and Pause(). |
22 | 22 |
23 #include "base/histogram.h" | 23 #include "base/histogram.h" |
24 #include "base/lock.h" | 24 #include "base/lock.h" |
25 #include "base/message_loop.h" | |
26 #include "base/process.h" | 25 #include "base/process.h" |
27 #include "base/shared_memory.h" | 26 #include "base/shared_memory.h" |
28 #include "base/waitable_event.h" | 27 #include "base/waitable_event.h" |
| 28 #include "chrome/browser/chrome_thread.h" |
29 #include "chrome/browser/renderer_host/audio_renderer_host.h" | 29 #include "chrome/browser/renderer_host/audio_renderer_host.h" |
30 #include "chrome/common/render_messages.h" | 30 #include "chrome/common/render_messages.h" |
31 #include "ipc/ipc_logging.h" | 31 #include "ipc/ipc_logging.h" |
32 | 32 |
33 namespace { | 33 namespace { |
34 | 34 |
35 // This constant governs the hardware audio buffer size, this value should be | 35 // This constant governs the hardware audio buffer size, this value should be |
36 // choosen carefully and is platform specific. | 36 // choosen carefully and is platform specific. |
37 const int kSamplesPerHardwarePacket = 8192; | 37 const int kSamplesPerHardwarePacket = 8192; |
38 | 38 |
(...skipping 285 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
324 } | 324 } |
325 } | 325 } |
326 | 326 |
327 void AudioRendererHost::IPCAudioSource::StartBuffering() { | 327 void AudioRendererHost::IPCAudioSource::StartBuffering() { |
328 SubmitPacketRequest(NULL); | 328 SubmitPacketRequest(NULL); |
329 } | 329 } |
330 | 330 |
331 //----------------------------------------------------------------------------- | 331 //----------------------------------------------------------------------------- |
332 // AudioRendererHost implementations. | 332 // AudioRendererHost implementations. |
333 | 333 |
334 AudioRendererHost::AudioRendererHost(MessageLoop* message_loop) | 334 AudioRendererHost::AudioRendererHost() |
335 : process_id_(0), | 335 : process_id_(0), |
336 process_handle_(0), | 336 process_handle_(0), |
337 ipc_sender_(NULL), | 337 ipc_sender_(NULL) { |
338 io_loop_(message_loop) { | |
339 // Make sure we perform actual initialization operations in the thread where | 338 // Make sure we perform actual initialization operations in the thread where |
340 // this object should live. | 339 // this object should live. |
341 io_loop_->PostTask(FROM_HERE, | 340 ChromeThread::PostTask( |
| 341 ChromeThread::IO, FROM_HERE, |
342 NewRunnableMethod(this, &AudioRendererHost::OnInitialized)); | 342 NewRunnableMethod(this, &AudioRendererHost::OnInitialized)); |
343 } | 343 } |
344 | 344 |
345 AudioRendererHost::~AudioRendererHost() { | 345 AudioRendererHost::~AudioRendererHost() { |
346 DCHECK(sources_.empty()); | 346 DCHECK(sources_.empty()); |
347 } | 347 } |
348 | 348 |
349 void AudioRendererHost::Destroy() { | 349 void AudioRendererHost::Destroy() { |
350 // Post a message to the thread where this object should live and do the | 350 // Post a message to the thread where this object should live and do the |
351 // actual operations there. | 351 // actual operations there. |
352 io_loop_->PostTask( | 352 ChromeThread::PostTask( |
353 FROM_HERE, NewRunnableMethod(this, &AudioRendererHost::OnDestroyed)); | 353 ChromeThread::IO, FROM_HERE, |
| 354 NewRunnableMethod(this, &AudioRendererHost::OnDestroyed)); |
354 } | 355 } |
355 | 356 |
356 // Event received when IPC channel is connected to the renderer process. | 357 // Event received when IPC channel is connected to the renderer process. |
357 void AudioRendererHost::IPCChannelConnected(int process_id, | 358 void AudioRendererHost::IPCChannelConnected(int process_id, |
358 base::ProcessHandle process_handle, | 359 base::ProcessHandle process_handle, |
359 IPC::Message::Sender* ipc_sender) { | 360 IPC::Message::Sender* ipc_sender) { |
360 DCHECK(MessageLoop::current() == io_loop_); | 361 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); |
361 process_id_ = process_id; | 362 process_id_ = process_id; |
362 process_handle_ = process_handle; | 363 process_handle_ = process_handle; |
363 ipc_sender_ = ipc_sender; | 364 ipc_sender_ = ipc_sender; |
364 } | 365 } |
365 | 366 |
366 // Event received when IPC channel is closing. | 367 // Event received when IPC channel is closing. |
367 void AudioRendererHost::IPCChannelClosing() { | 368 void AudioRendererHost::IPCChannelClosing() { |
368 DCHECK(MessageLoop::current() == io_loop_); | 369 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); |
369 ipc_sender_ = NULL; | 370 ipc_sender_ = NULL; |
370 process_handle_ = 0; | 371 process_handle_ = 0; |
371 process_id_ = 0; | 372 process_id_ = 0; |
372 DestroyAllSources(); | 373 DestroyAllSources(); |
373 } | 374 } |
374 | 375 |
375 bool AudioRendererHost::OnMessageReceived(const IPC::Message& message, | 376 bool AudioRendererHost::OnMessageReceived(const IPC::Message& message, |
376 bool* message_was_ok) { | 377 bool* message_was_ok) { |
377 if (!IsAudioRendererHostMessage(message)) | 378 if (!IsAudioRendererHostMessage(message)) |
378 return false; | 379 return false; |
(...skipping 25 matching lines...) Expand all Loading... |
404 return true; | 405 return true; |
405 default: | 406 default: |
406 break; | 407 break; |
407 } | 408 } |
408 return false; | 409 return false; |
409 } | 410 } |
410 | 411 |
411 void AudioRendererHost::OnCreateStream( | 412 void AudioRendererHost::OnCreateStream( |
412 const IPC::Message& msg, int stream_id, | 413 const IPC::Message& msg, int stream_id, |
413 const ViewHostMsg_Audio_CreateStream& params) { | 414 const ViewHostMsg_Audio_CreateStream& params) { |
414 DCHECK(MessageLoop::current() == io_loop_); | 415 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); |
415 DCHECK(Lookup(msg.routing_id(), stream_id) == NULL); | 416 DCHECK(Lookup(msg.routing_id(), stream_id) == NULL); |
416 | 417 |
417 IPCAudioSource* source = IPCAudioSource::CreateIPCAudioSource( | 418 IPCAudioSource* source = IPCAudioSource::CreateIPCAudioSource( |
418 this, | 419 this, |
419 process_id_, | 420 process_id_, |
420 msg.routing_id(), | 421 msg.routing_id(), |
421 stream_id, | 422 stream_id, |
422 process_handle_, | 423 process_handle_, |
423 params.format, | 424 params.format, |
424 params.channels, | 425 params.channels, |
425 params.sample_rate, | 426 params.sample_rate, |
426 params.bits_per_sample, | 427 params.bits_per_sample, |
427 params.packet_size, | 428 params.packet_size, |
428 params.buffer_capacity); | 429 params.buffer_capacity); |
429 | 430 |
430 // If we have created the source successfully, adds it to the map. | 431 // If we have created the source successfully, adds it to the map. |
431 if (source) { | 432 if (source) { |
432 sources_.insert( | 433 sources_.insert( |
433 std::make_pair( | 434 std::make_pair( |
434 SourceID(source->route_id(), source->stream_id()), source)); | 435 SourceID(source->route_id(), source->stream_id()), source)); |
435 } else { | 436 } else { |
436 SendErrorMessage(msg.routing_id(), stream_id); | 437 SendErrorMessage(msg.routing_id(), stream_id); |
437 } | 438 } |
438 } | 439 } |
439 | 440 |
440 void AudioRendererHost::OnPlayStream(const IPC::Message& msg, int stream_id) { | 441 void AudioRendererHost::OnPlayStream(const IPC::Message& msg, int stream_id) { |
441 DCHECK(MessageLoop::current() == io_loop_); | 442 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); |
442 IPCAudioSource* source = Lookup(msg.routing_id(), stream_id); | 443 IPCAudioSource* source = Lookup(msg.routing_id(), stream_id); |
443 if (source) { | 444 if (source) { |
444 source->Play(); | 445 source->Play(); |
445 } else { | 446 } else { |
446 SendErrorMessage(msg.routing_id(), stream_id); | 447 SendErrorMessage(msg.routing_id(), stream_id); |
447 } | 448 } |
448 } | 449 } |
449 | 450 |
450 void AudioRendererHost::OnPauseStream(const IPC::Message& msg, int stream_id) { | 451 void AudioRendererHost::OnPauseStream(const IPC::Message& msg, int stream_id) { |
451 DCHECK(MessageLoop::current() == io_loop_); | 452 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); |
452 IPCAudioSource* source = Lookup(msg.routing_id(), stream_id); | 453 IPCAudioSource* source = Lookup(msg.routing_id(), stream_id); |
453 if (source) { | 454 if (source) { |
454 source->Pause(); | 455 source->Pause(); |
455 } else { | 456 } else { |
456 SendErrorMessage(msg.routing_id(), stream_id); | 457 SendErrorMessage(msg.routing_id(), stream_id); |
457 } | 458 } |
458 } | 459 } |
459 | 460 |
460 void AudioRendererHost::OnCloseStream(const IPC::Message& msg, int stream_id) { | 461 void AudioRendererHost::OnCloseStream(const IPC::Message& msg, int stream_id) { |
461 DCHECK(MessageLoop::current() == io_loop_); | 462 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); |
462 IPCAudioSource* source = Lookup(msg.routing_id(), stream_id); | 463 IPCAudioSource* source = Lookup(msg.routing_id(), stream_id); |
463 if (source) { | 464 if (source) { |
464 DestroySource(source); | 465 DestroySource(source); |
465 } | 466 } |
466 } | 467 } |
467 | 468 |
468 void AudioRendererHost::OnSetVolume(const IPC::Message& msg, int stream_id, | 469 void AudioRendererHost::OnSetVolume(const IPC::Message& msg, int stream_id, |
469 double left_channel, double right_channel) { | 470 double left_channel, double right_channel) { |
470 DCHECK(MessageLoop::current() == io_loop_); | 471 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); |
471 IPCAudioSource* source = Lookup(msg.routing_id(), stream_id); | 472 IPCAudioSource* source = Lookup(msg.routing_id(), stream_id); |
472 if (source) { | 473 if (source) { |
473 source->SetVolume(left_channel, right_channel); | 474 source->SetVolume(left_channel, right_channel); |
474 } else { | 475 } else { |
475 SendErrorMessage(msg.routing_id(), stream_id); | 476 SendErrorMessage(msg.routing_id(), stream_id); |
476 } | 477 } |
477 } | 478 } |
478 | 479 |
479 void AudioRendererHost::OnGetVolume(const IPC::Message& msg, int stream_id) { | 480 void AudioRendererHost::OnGetVolume(const IPC::Message& msg, int stream_id) { |
480 DCHECK(MessageLoop::current() == io_loop_); | 481 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); |
481 IPCAudioSource* source = Lookup(msg.routing_id(), stream_id); | 482 IPCAudioSource* source = Lookup(msg.routing_id(), stream_id); |
482 if (source) { | 483 if (source) { |
483 source->GetVolume(); | 484 source->GetVolume(); |
484 } else { | 485 } else { |
485 SendErrorMessage(msg.routing_id(), stream_id); | 486 SendErrorMessage(msg.routing_id(), stream_id); |
486 } | 487 } |
487 } | 488 } |
488 | 489 |
489 void AudioRendererHost::OnNotifyPacketReady(const IPC::Message& msg, | 490 void AudioRendererHost::OnNotifyPacketReady(const IPC::Message& msg, |
490 int stream_id, size_t packet_size) { | 491 int stream_id, size_t packet_size) { |
491 DCHECK(MessageLoop::current() == io_loop_); | 492 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); |
492 IPCAudioSource* source = Lookup(msg.routing_id(), stream_id); | 493 IPCAudioSource* source = Lookup(msg.routing_id(), stream_id); |
493 if (source) { | 494 if (source) { |
494 source->NotifyPacketReady(packet_size); | 495 source->NotifyPacketReady(packet_size); |
495 } else { | 496 } else { |
496 SendErrorMessage(msg.routing_id(), stream_id); | 497 SendErrorMessage(msg.routing_id(), stream_id); |
497 } | 498 } |
498 } | 499 } |
499 | 500 |
500 void AudioRendererHost::OnInitialized() { | 501 void AudioRendererHost::OnInitialized() { |
501 DCHECK(MessageLoop::current() == io_loop_); | 502 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); |
502 // Increase the ref count of this object so it is active until we do | 503 // Increase the ref count of this object so it is active until we do |
503 // Release(). | 504 // Release(). |
504 AddRef(); | 505 AddRef(); |
505 } | 506 } |
506 | 507 |
507 void AudioRendererHost::OnDestroyed() { | 508 void AudioRendererHost::OnDestroyed() { |
508 DCHECK(MessageLoop::current() == io_loop_); | 509 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); |
509 ipc_sender_ = NULL; | 510 ipc_sender_ = NULL; |
510 process_handle_ = 0; | 511 process_handle_ = 0; |
511 process_id_ = 0; | 512 process_id_ = 0; |
512 DestroyAllSources(); | 513 DestroyAllSources(); |
513 // Decrease the reference to this object, which may lead to self-destruction. | 514 // Decrease the reference to this object, which may lead to self-destruction. |
514 Release(); | 515 Release(); |
515 } | 516 } |
516 | 517 |
517 void AudioRendererHost::OnSend(IPC::Message* message) { | 518 void AudioRendererHost::OnSend(IPC::Message* message) { |
518 DCHECK(MessageLoop::current() == io_loop_); | 519 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); |
519 if (ipc_sender_) { | 520 if (ipc_sender_) { |
520 ipc_sender_->Send(message); | 521 ipc_sender_->Send(message); |
521 } | 522 } |
522 } | 523 } |
523 | 524 |
524 void AudioRendererHost::OnDestroySource(IPCAudioSource* source) { | 525 void AudioRendererHost::OnDestroySource(IPCAudioSource* source) { |
525 DCHECK(MessageLoop::current() == io_loop_); | 526 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); |
526 if (source) { | 527 if (source) { |
527 sources_.erase(SourceID(source->route_id(), source->stream_id())); | 528 sources_.erase(SourceID(source->route_id(), source->stream_id())); |
528 source->Close(); | 529 source->Close(); |
529 delete source; | 530 delete source; |
530 } | 531 } |
531 } | 532 } |
532 | 533 |
533 void AudioRendererHost::DestroyAllSources() { | 534 void AudioRendererHost::DestroyAllSources() { |
534 DCHECK(MessageLoop::current() == io_loop_); | 535 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); |
535 std::vector<IPCAudioSource*> sources; | 536 std::vector<IPCAudioSource*> sources; |
536 for (SourceMap::iterator i = sources_.begin(); i != sources_.end(); ++i) { | 537 for (SourceMap::iterator i = sources_.begin(); i != sources_.end(); ++i) { |
537 sources.push_back(i->second); | 538 sources.push_back(i->second); |
538 } | 539 } |
539 for (size_t i = 0; i < sources.size(); ++i) { | 540 for (size_t i = 0; i < sources.size(); ++i) { |
540 DestroySource(sources[i]); | 541 DestroySource(sources[i]); |
541 } | 542 } |
542 DCHECK(sources_.empty()); | 543 DCHECK(sources_.empty()); |
543 } | 544 } |
544 | 545 |
545 AudioRendererHost::IPCAudioSource* AudioRendererHost::Lookup(int route_id, | 546 AudioRendererHost::IPCAudioSource* AudioRendererHost::Lookup(int route_id, |
546 int stream_id) { | 547 int stream_id) { |
547 DCHECK(MessageLoop::current() == io_loop_); | 548 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); |
548 SourceMap::iterator i = sources_.find(SourceID(route_id, stream_id)); | 549 SourceMap::iterator i = sources_.find(SourceID(route_id, stream_id)); |
549 if (i != sources_.end()) | 550 if (i != sources_.end()) |
550 return i->second; | 551 return i->second; |
551 return NULL; | 552 return NULL; |
552 } | 553 } |
553 | 554 |
554 void AudioRendererHost::Send(IPC::Message* message) { | 555 void AudioRendererHost::Send(IPC::Message* message) { |
555 if (MessageLoop::current() == io_loop_) { | 556 if (ChromeThread::CurrentlyOn(ChromeThread::IO)) { |
556 OnSend(message); | 557 OnSend(message); |
557 } else { | 558 } else { |
558 // TODO(hclam): make sure it's always safe to post a task to IO loop. | 559 ChromeThread::PostTask( |
559 // It is possible that IO message loop is destroyed but there's still some | 560 ChromeThread::IO, FROM_HERE, |
560 // dangling audio hardware threads that try to call this method. | |
561 io_loop_->PostTask(FROM_HERE, | |
562 NewRunnableMethod(this, &AudioRendererHost::OnSend, message)); | 561 NewRunnableMethod(this, &AudioRendererHost::OnSend, message)); |
563 } | 562 } |
564 } | 563 } |
565 | 564 |
566 void AudioRendererHost::SendErrorMessage(int32 render_view_id, | 565 void AudioRendererHost::SendErrorMessage(int32 render_view_id, |
567 int32 stream_id) { | 566 int32 stream_id) { |
568 ViewMsg_AudioStreamState state; | 567 ViewMsg_AudioStreamState state; |
569 state.state = ViewMsg_AudioStreamState::kError; | 568 state.state = ViewMsg_AudioStreamState::kError; |
570 Send(new ViewMsg_NotifyAudioStreamStateChanged( | 569 Send(new ViewMsg_NotifyAudioStreamStateChanged( |
571 render_view_id, stream_id, state)); | 570 render_view_id, stream_id, state)); |
572 } | 571 } |
573 | 572 |
574 void AudioRendererHost::DestroySource(IPCAudioSource* source) { | 573 void AudioRendererHost::DestroySource(IPCAudioSource* source) { |
575 if (MessageLoop::current() == io_loop_) { | 574 if (ChromeThread::CurrentlyOn(ChromeThread::IO)) { |
576 OnDestroySource(source); | 575 OnDestroySource(source); |
577 } else { | 576 } else { |
578 // TODO(hclam): make sure it's always safe to post a task to IO loop. | 577 ChromeThread::PostTask( |
579 // It is possible that IO message loop is destroyed but there's still some | 578 ChromeThread::IO, FROM_HERE, |
580 // dangling audio hardware threads that try to call this method. | 579 NewRunnableMethod(this, &AudioRendererHost::OnDestroySource, source)); |
581 io_loop_->PostTask(FROM_HERE, | |
582 NewRunnableMethod(this, &AudioRendererHost::OnDestroySource, source)); | |
583 } | 580 } |
584 } | 581 } |
OLD | NEW |