| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "media/audio/audio_device_thread.h" |
| |
| #include <algorithm> |
| |
| #include "base/bind.h" |
| #include "base/logging.h" |
| #include "base/memory/aligned_memory.h" |
| #include "base/message_loop/message_loop.h" |
| #include "base/threading/platform_thread.h" |
| #include "base/threading/thread_restrictions.h" |
| #include "media/base/audio_bus.h" |
| |
| using base::PlatformThread; |
| |
| namespace media { |
| |
| // The actual worker thread implementation. It's very bare bones and much |
| // simpler than SimpleThread (no synchronization in Start, etc) and supports |
| // joining the thread handle asynchronously via a provided message loop even |
| // after the Thread object itself has been deleted. |
| class AudioDeviceThread::Thread |
| : public PlatformThread::Delegate, |
| public base::RefCountedThreadSafe<AudioDeviceThread::Thread> { |
| public: |
| Thread(AudioDeviceThread::Callback* callback, |
| base::SyncSocket::Handle socket, |
| const char* thread_name, |
| bool synchronized_buffers); |
| |
| void Start(); |
| |
| // Stops the thread. If |loop_for_join| is non-NULL, the function posts |
| // a task to join (close) the thread handle later instead of waiting for |
| // the thread. If loop_for_join is NULL, then the function waits |
| // synchronously for the thread to terminate. |
| void Stop(base::MessageLoop* loop_for_join); |
| |
| private: |
| friend class base::RefCountedThreadSafe<AudioDeviceThread::Thread>; |
| virtual ~Thread(); |
| |
| // Overrides from PlatformThread::Delegate. |
| virtual void ThreadMain() OVERRIDE; |
| |
| // Runs the loop that reads from the socket. |
| void Run(); |
| |
| private: |
| base::PlatformThreadHandle thread_; |
| AudioDeviceThread::Callback* callback_; |
| base::CancelableSyncSocket socket_; |
| base::Lock callback_lock_; |
| const char* thread_name_; |
| const bool synchronized_buffers_; |
| |
| DISALLOW_COPY_AND_ASSIGN(Thread); |
| }; |
| |
| // AudioDeviceThread implementation |
| |
| AudioDeviceThread::AudioDeviceThread() { |
| } |
| |
| AudioDeviceThread::~AudioDeviceThread() { DCHECK(!thread_.get()); } |
| |
| void AudioDeviceThread::Start(AudioDeviceThread::Callback* callback, |
| base::SyncSocket::Handle socket, |
| const char* thread_name, |
| bool synchronized_buffers) { |
| base::AutoLock auto_lock(thread_lock_); |
| CHECK(!thread_); |
| thread_ = new AudioDeviceThread::Thread( |
| callback, socket, thread_name, synchronized_buffers); |
| thread_->Start(); |
| } |
| |
| void AudioDeviceThread::Stop(base::MessageLoop* loop_for_join) { |
| base::AutoLock auto_lock(thread_lock_); |
| if (thread_.get()) { |
| thread_->Stop(loop_for_join); |
| thread_ = NULL; |
| } |
| } |
| |
| bool AudioDeviceThread::IsStopped() { |
| base::AutoLock auto_lock(thread_lock_); |
| return !thread_; |
| } |
| |
| // AudioDeviceThread::Thread implementation |
| AudioDeviceThread::Thread::Thread(AudioDeviceThread::Callback* callback, |
| base::SyncSocket::Handle socket, |
| const char* thread_name, |
| bool synchronized_buffers) |
| : thread_(), |
| callback_(callback), |
| socket_(socket), |
| thread_name_(thread_name), |
| synchronized_buffers_(synchronized_buffers) { |
| } |
| |
| AudioDeviceThread::Thread::~Thread() { |
| DCHECK(thread_.is_null()); |
| } |
| |
| void AudioDeviceThread::Thread::Start() { |
| base::AutoLock auto_lock(callback_lock_); |
| DCHECK(thread_.is_null()); |
| // This reference will be released when the thread exists. |
| AddRef(); |
| |
| PlatformThread::CreateWithPriority(0, this, &thread_, |
| base::kThreadPriority_RealtimeAudio); |
| CHECK(!thread_.is_null()); |
| } |
| |
| void AudioDeviceThread::Thread::Stop(base::MessageLoop* loop_for_join) { |
| socket_.Shutdown(); |
| |
| base::PlatformThreadHandle thread = base::PlatformThreadHandle(); |
| |
| { // NOLINT |
| base::AutoLock auto_lock(callback_lock_); |
| callback_ = NULL; |
| std::swap(thread, thread_); |
| } |
| |
| if (!thread.is_null()) { |
| if (loop_for_join) { |
| loop_for_join->PostTask(FROM_HERE, |
| base::Bind(&base::PlatformThread::Join, thread)); |
| } else { |
| base::PlatformThread::Join(thread); |
| } |
| } |
| } |
| |
| void AudioDeviceThread::Thread::ThreadMain() { |
| PlatformThread::SetName(thread_name_); |
| |
| // Singleton access is safe from this thread as long as callback is non-NULL. |
| // The callback is the only point where the thread calls out to 'unknown' code |
| // that might touch singletons and the lifetime of the callback is controlled |
| // by another thread on which singleton access is OK as well. |
| base::ThreadRestrictions::SetSingletonAllowed(true); |
| |
| { // NOLINT |
| base::AutoLock auto_lock(callback_lock_); |
| if (callback_) |
| callback_->InitializeOnAudioThread(); |
| } |
| |
| Run(); |
| |
| // Release the reference for the thread. Note that after this, the Thread |
| // instance will most likely be deleted. |
| Release(); |
| } |
| |
| void AudioDeviceThread::Thread::Run() { |
| uint32 buffer_index = 0; |
| while (true) { |
| int pending_data = 0; |
| size_t bytes_read = socket_.Receive(&pending_data, sizeof(pending_data)); |
| if (bytes_read != sizeof(pending_data)) { |
| DCHECK_EQ(bytes_read, 0U); |
| break; |
| } |
| |
| { |
| base::AutoLock auto_lock(callback_lock_); |
| if (callback_) |
| callback_->Process(pending_data); |
| } |
| |
| // Let the other end know which buffer we just filled. The buffer index is |
| // used to ensure the other end is getting the buffer it expects. For more |
| // details on how this works see AudioSyncReader::WaitUntilDataIsReady(). |
| if (synchronized_buffers_) { |
| ++buffer_index; |
| size_t bytes_sent = socket_.Send(&buffer_index, sizeof(buffer_index)); |
| if (bytes_sent != sizeof(buffer_index)) |
| break; |
| } |
| } |
| } |
| |
| // AudioDeviceThread::Callback implementation |
| |
| AudioDeviceThread::Callback::Callback( |
| const AudioParameters& audio_parameters, |
| base::SharedMemoryHandle memory, |
| int memory_length, |
| int total_segments) |
| : audio_parameters_(audio_parameters), |
| samples_per_ms_(audio_parameters.sample_rate() / 1000), |
| bytes_per_ms_(audio_parameters.channels() * |
| (audio_parameters_.bits_per_sample() / 8) * |
| samples_per_ms_), |
| shared_memory_(memory, false), |
| memory_length_(memory_length), |
| total_segments_(total_segments) { |
| CHECK_NE(bytes_per_ms_, 0); // Catch division by zero early. |
| CHECK_NE(samples_per_ms_, 0); |
| CHECK_GT(total_segments_, 0); |
| CHECK_EQ(memory_length_ % total_segments_, 0); |
| segment_length_ = memory_length_ / total_segments_; |
| } |
| |
| AudioDeviceThread::Callback::~Callback() {} |
| |
| void AudioDeviceThread::Callback::InitializeOnAudioThread() { |
| MapSharedMemory(); |
| CHECK(shared_memory_.memory()); |
| } |
| |
| } // namespace media. |