// 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.