blob: daf908556d8edf221f61fda6d4cd38fc41d9fbd2 [file] [log] [blame]
// 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.