changes for proxy audio
- includes Darin's changes to move StreamCreated() to main thread - callback for delivering handles to proxy - changes to trusted interface BUG=none TEST=chrome/src/ppapi/examples/audio/audio.cc Review URL: http://codereview.chromium.org/5202002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@67354 0039d316-1c4b-4281-b951-d872f2087c98
This commit is contained in:
chrome/renderer
ppapi
webkit/glue/plugins
@ -123,10 +123,12 @@ class PlatformContext3DImpl : public pepper::PluginDelegate::PlatformContext3D {
|
||||
|
||||
class PlatformAudioImpl
|
||||
: public pepper::PluginDelegate::PlatformAudio,
|
||||
public AudioMessageFilter::Delegate {
|
||||
public AudioMessageFilter::Delegate,
|
||||
public base::RefCountedThreadSafe<PlatformAudioImpl> {
|
||||
public:
|
||||
explicit PlatformAudioImpl(scoped_refptr<AudioMessageFilter> filter)
|
||||
: client_(NULL), filter_(filter), stream_id_(0) {
|
||||
: client_(NULL), filter_(filter), stream_id_(0),
|
||||
main_message_loop_(MessageLoop::current()) {
|
||||
DCHECK(filter_);
|
||||
}
|
||||
|
||||
@ -177,6 +179,8 @@ class PlatformAudioImpl
|
||||
// Our ID on the MessageFilter.
|
||||
int32 stream_id_;
|
||||
|
||||
MessageLoop* main_message_loop_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(PlatformAudioImpl);
|
||||
};
|
||||
|
||||
@ -297,7 +301,15 @@ void PlatformAudioImpl::OnLowLatencyCreated(
|
||||
#endif
|
||||
DCHECK(length);
|
||||
|
||||
client_->StreamCreated(handle, length, socket_handle);
|
||||
if (MessageLoop::current() == main_message_loop_) {
|
||||
if (client_) {
|
||||
client_->StreamCreated(handle, length, socket_handle);
|
||||
}
|
||||
} else {
|
||||
main_message_loop_->PostTask(FROM_HERE,
|
||||
NewRunnableMethod(this, &PlatformAudioImpl::OnLowLatencyCreated,
|
||||
handle, socket_handle, length));
|
||||
}
|
||||
}
|
||||
|
||||
void PlatformAudioImpl::ShutDown() {
|
||||
@ -309,6 +321,10 @@ void PlatformAudioImpl::ShutDown() {
|
||||
filter_->RemoveDelegate(stream_id_);
|
||||
stream_id_ = 0;
|
||||
client_ = NULL;
|
||||
|
||||
// Release on the IO thread so that we avoid race problems with
|
||||
// OnLowLatencyCreated.
|
||||
filter_->message_loop()->ReleaseSoon(FROM_HERE, this);
|
||||
}
|
||||
|
||||
// Implements the VideoDecoder.
|
||||
@ -560,9 +576,11 @@ void PepperPluginDelegateImpl::SelectedFindResultChanged(int identifier,
|
||||
pepper::PluginDelegate::PlatformAudio* PepperPluginDelegateImpl::CreateAudio(
|
||||
uint32_t sample_rate, uint32_t sample_count,
|
||||
pepper::PluginDelegate::PlatformAudio::Client* client) {
|
||||
scoped_ptr<PlatformAudioImpl> audio(
|
||||
scoped_refptr<PlatformAudioImpl> audio(
|
||||
new PlatformAudioImpl(render_view_->audio_message_filter()));
|
||||
if (audio->Initialize(sample_rate, sample_count, client)) {
|
||||
|
||||
// Also note ReleaseSoon invoked in PlatformAudioImpl::ShutDown().
|
||||
return audio.release();
|
||||
} else {
|
||||
return NULL;
|
||||
|
@ -5,23 +5,37 @@
|
||||
#ifndef PPAPI_C_DEV_PPB_AUDIO_TRUSTED_DEV_H_
|
||||
#define PPAPI_C_DEV_PPB_AUDIO_TRUSTED_DEV_H_
|
||||
|
||||
#include "ppapi/c/pp_completion_callback.h"
|
||||
#include "ppapi/c/pp_instance.h"
|
||||
#include "ppapi/c/pp_resource.h"
|
||||
|
||||
#define PPB_AUDIO_TRUSTED_DEV_INTERFACE "PPB_AudioTrusted(Dev);0.1"
|
||||
#define PPB_AUDIO_TRUSTED_DEV_INTERFACE "PPB_AudioTrusted(Dev);0.2"
|
||||
|
||||
// This interface is used to get access to the audio buffer and a socket on
|
||||
// which the client can block until the audio is ready to accept more data.
|
||||
// This interface should be used by NaCl to implement the Audio interface.
|
||||
// This interface is to be used by proxy implementations. All
|
||||
// functions should be called from the main thread only. The
|
||||
// resource returned is an Audio resource; most of the PPB_Audio_Dev
|
||||
// interface is also usable on this resource.
|
||||
struct PPB_AudioTrusted_Dev {
|
||||
// Returns a Buffer object that has the audio buffer.
|
||||
PP_Resource (*GetBuffer)(PP_Resource audio);
|
||||
// Returns an audio resource.
|
||||
PP_Resource (*CreateTrusted)(PP_Instance instance);
|
||||
|
||||
// Returns a select()-able/Wait()-able OS-specific descriptor. The browser
|
||||
// will put a byte on the socket each time the buffer is ready to be filled.
|
||||
// The plugin can then implement its own audio thread using select()/poll() to
|
||||
// block until the browser is ready to receive data.
|
||||
int (*GetOSDescriptor)(PP_Resource audio);
|
||||
// Opens a paused audio interface, used by trusted side of proxy.
|
||||
// Returns PP_ERROR_WOULD_BLOCK on success, and invokes
|
||||
// the |create_callback| asynchronously to complete.
|
||||
// As this function should always be invoked from the main thread,
|
||||
// do not use the blocking variant of PP_CompletionCallback.
|
||||
int32_t (*Open)(PP_Resource audio, PP_Resource config,
|
||||
struct PP_CompletionCallback create_callback);
|
||||
|
||||
// Get the sync socket. Use once Open has completed.
|
||||
// Returns PP_OK on success.
|
||||
int32_t (*GetSyncSocket)(PP_Resource audio, int* sync_socket);
|
||||
|
||||
// Get the shared memory interface. Use once Open has completed.
|
||||
// Returns PP_OK on success.
|
||||
int32_t (*GetSharedMemory)(PP_Resource audio,
|
||||
int* shm_handle,
|
||||
int32_t* shm_size);
|
||||
};
|
||||
|
||||
#endif // PPAPI_C_DEV_PPB_AUDIO_TRUSTED_DEV_H_
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include <limits>
|
||||
|
||||
#include "ppapi/cpp/dev/audio_dev.h"
|
||||
#include "ppapi/cpp/dev/audio_config_dev.h"
|
||||
#include "ppapi/cpp/instance.h"
|
||||
#include "ppapi/cpp/module.h"
|
||||
|
||||
@ -17,6 +18,7 @@ const double frequency_r = 1000;
|
||||
// This sample frequency is guaranteed to work.
|
||||
const PP_AudioSampleRate_Dev sample_frequency = PP_AUDIOSAMPLERATE_44100;
|
||||
const uint32_t sample_count = 4096;
|
||||
uint32_t obtained_sample_count = 0;
|
||||
|
||||
class MyInstance : public pp::Instance {
|
||||
public:
|
||||
@ -26,16 +28,16 @@ class MyInstance : public pp::Instance {
|
||||
}
|
||||
|
||||
virtual bool Init(uint32_t argc, const char* argn[], const char* argv[]) {
|
||||
audio_ = pp::Audio_Dev(
|
||||
*this, pp::AudioConfig_Dev(sample_frequency, sample_count),
|
||||
SineWaveCallback, this);
|
||||
pp::AudioConfig_Dev config;
|
||||
obtained_sample_count = pp::AudioConfig_Dev::RecommendSampleFrameCount(
|
||||
sample_count);
|
||||
config = pp::AudioConfig_Dev(sample_frequency, obtained_sample_count);
|
||||
audio_ = pp::Audio_Dev(*this, config, SineWaveCallback, this);
|
||||
return audio_.StartPlayback();
|
||||
}
|
||||
|
||||
private:
|
||||
static void SineWaveCallback(void* samples,
|
||||
size_t buffer_size_in_bytes,
|
||||
void* thiz) {
|
||||
static void SineWaveCallback(void* samples, size_t num_bytes, void* thiz) {
|
||||
const double th_l = 2 * 3.141592653589 * frequency_l / sample_frequency;
|
||||
const double th_r = 2 * 3.141592653589 * frequency_r / sample_frequency;
|
||||
|
||||
@ -43,12 +45,10 @@ class MyInstance : public pp::Instance {
|
||||
size_t t = reinterpret_cast<MyInstance*>(thiz)->audio_time_;
|
||||
|
||||
uint16_t* buf = reinterpret_cast<uint16_t*>(samples);
|
||||
for (size_t buffer_index = 0u;
|
||||
buffer_index < buffer_size_in_bytes;
|
||||
buffer_index += 2) {
|
||||
*buf++ = static_cast<uint16_t>(std::sin(th_l * t)
|
||||
for (size_t sample = 0; sample < obtained_sample_count; ++sample) {
|
||||
*buf++ = static_cast<uint16_t>(sin(th_l * t)
|
||||
* std::numeric_limits<uint16_t>::max());
|
||||
*buf++ = static_cast<uint16_t>(std::sin(th_r * t++)
|
||||
*buf++ = static_cast<uint16_t>(sin(th_r * t++)
|
||||
* std::numeric_limits<uint16_t>::max());
|
||||
}
|
||||
reinterpret_cast<MyInstance*>(thiz)->audio_time_ = t;
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "base/logging.h"
|
||||
#include "ppapi/c/dev/ppb_audio_dev.h"
|
||||
#include "ppapi/c/dev/ppb_audio_trusted_dev.h"
|
||||
#include "ppapi/c/pp_completion_callback.h"
|
||||
#include "webkit/glue/plugins/pepper_common.h"
|
||||
|
||||
namespace pepper {
|
||||
@ -77,13 +78,15 @@ const PPB_AudioConfig_Dev ppb_audioconfig = {
|
||||
// PPB_Audio -------------------------------------------------------------------
|
||||
|
||||
PP_Resource Create(PP_Instance instance_id, PP_Resource config_id,
|
||||
PPB_Audio_Callback callback, void* user_data) {
|
||||
PPB_Audio_Callback user_callback, void* user_data) {
|
||||
PluginInstance* instance = ResourceTracker::Get()->GetInstance(instance_id);
|
||||
if (!instance)
|
||||
return 0;
|
||||
// TODO(neb): Require callback to be present for untrusted plugins.
|
||||
scoped_refptr<Audio> audio(new Audio(instance->module()));
|
||||
if (!audio->Init(instance->delegate(), config_id, callback, user_data))
|
||||
if (!user_callback)
|
||||
return 0;
|
||||
scoped_refptr<Audio> audio(new Audio(instance->module(), instance_id));
|
||||
if (!audio->Init(instance->delegate(), config_id,
|
||||
user_callback, user_data))
|
||||
return 0;
|
||||
return audio->GetReference();
|
||||
}
|
||||
@ -118,19 +121,50 @@ const PPB_Audio_Dev ppb_audio = {
|
||||
|
||||
// PPB_AudioTrusted ------------------------------------------------------------
|
||||
|
||||
PP_Resource GetBuffer(PP_Resource audio_id) {
|
||||
// TODO(neb): Implement me!
|
||||
return 0;
|
||||
PP_Resource CreateTrusted(PP_Instance instance_id) {
|
||||
PluginInstance* instance = ResourceTracker::Get()->GetInstance(instance_id);
|
||||
if (!instance)
|
||||
return 0;
|
||||
scoped_refptr<Audio> audio(new Audio(instance->module(), instance_id));
|
||||
return audio->GetReference();
|
||||
}
|
||||
|
||||
int GetOSDescriptor(PP_Resource audio_id) {
|
||||
// TODO(neb): Implement me!
|
||||
return -1;
|
||||
int32_t Open(PP_Resource audio_id,
|
||||
PP_Resource config_id,
|
||||
PP_CompletionCallback created) {
|
||||
scoped_refptr<Audio> audio = Resource::GetAs<Audio>(audio_id);
|
||||
if (!audio)
|
||||
return PP_ERROR_BADRESOURCE;
|
||||
if (!created.func)
|
||||
return PP_ERROR_BADARGUMENT;
|
||||
PP_Instance instance_id = audio->pp_instance();
|
||||
PluginInstance* instance = ResourceTracker::Get()->GetInstance(instance_id);
|
||||
if (!instance)
|
||||
return PP_ERROR_FAILED;
|
||||
return audio->Open(instance->delegate(), config_id, created);
|
||||
}
|
||||
|
||||
int32_t GetSyncSocket(PP_Resource audio_id, int* sync_socket) {
|
||||
scoped_refptr<Audio> audio = Resource::GetAs<Audio>(audio_id);
|
||||
if (audio)
|
||||
return audio->GetSyncSocket(sync_socket);
|
||||
return PP_ERROR_BADRESOURCE;
|
||||
}
|
||||
|
||||
int32_t GetSharedMemory(PP_Resource audio_id,
|
||||
int* shm_handle,
|
||||
int32_t* shm_size) {
|
||||
scoped_refptr<Audio> audio = Resource::GetAs<Audio>(audio_id);
|
||||
if (audio)
|
||||
return audio->GetSharedMemory(shm_handle, shm_size);
|
||||
return PP_ERROR_BADRESOURCE;
|
||||
}
|
||||
|
||||
const PPB_AudioTrusted_Dev ppb_audiotrusted = {
|
||||
&GetBuffer,
|
||||
&GetOSDescriptor
|
||||
&CreateTrusted,
|
||||
&Open,
|
||||
&GetSyncSocket,
|
||||
&GetSharedMemory,
|
||||
};
|
||||
|
||||
} // namespace
|
||||
@ -165,25 +199,38 @@ AudioConfig* AudioConfig::AsAudioConfig() {
|
||||
|
||||
// Audio -----------------------------------------------------------------------
|
||||
|
||||
Audio::Audio(PluginModule* module)
|
||||
Audio::Audio(PluginModule* module, PP_Instance instance_id)
|
||||
: Resource(module),
|
||||
playing_(false),
|
||||
pp_instance_(instance_id),
|
||||
audio_(NULL),
|
||||
socket_(NULL),
|
||||
shared_memory_(NULL),
|
||||
shared_memory_size_(0),
|
||||
callback_(NULL),
|
||||
user_data_(NULL) {
|
||||
user_data_(NULL),
|
||||
create_callback_pending_(false) {
|
||||
create_callback_ = PP_MakeCompletionCallback(NULL, NULL);
|
||||
}
|
||||
|
||||
Audio::~Audio() {
|
||||
// Calling ShutDown() makes sure StreamCreated cannot be called anymore.
|
||||
audio_->ShutDown();
|
||||
audio_ = NULL;
|
||||
|
||||
// Closing the socket causes the thread to exit - wait for it.
|
||||
socket_->Close();
|
||||
if (audio_thread_.get()) {
|
||||
audio_thread_->Join();
|
||||
audio_thread_.reset();
|
||||
}
|
||||
|
||||
// If the completion callback hasn't fired yet, do so here
|
||||
// with an error condition.
|
||||
if (create_callback_pending_) {
|
||||
PP_RunCompletionCallback(&create_callback_, PP_ERROR_ABORTED);
|
||||
create_callback_pending_ = false;
|
||||
}
|
||||
// Shared memory destructor will unmap the memory automatically.
|
||||
}
|
||||
|
||||
@ -199,19 +246,73 @@ Audio* Audio::AsAudio() {
|
||||
return this;
|
||||
}
|
||||
|
||||
bool Audio::Init(PluginDelegate* plugin_delegate, PP_Resource config_id,
|
||||
bool Audio::Init(PluginDelegate* plugin_delegate,
|
||||
PP_Resource config_id,
|
||||
PPB_Audio_Callback callback, void* user_data) {
|
||||
CHECK(!audio_.get());
|
||||
CHECK(!audio_);
|
||||
config_ = Resource::GetAs<AudioConfig>(config_id);
|
||||
if (!config_)
|
||||
return false;
|
||||
callback_ = callback;
|
||||
user_data_ = user_data;
|
||||
// When the stream is created, we'll get called back in StreamCreated().
|
||||
audio_.reset(plugin_delegate->CreateAudio(config_->sample_rate(),
|
||||
config_->sample_frame_count(),
|
||||
this));
|
||||
return audio_.get() != NULL;
|
||||
|
||||
// When the stream is created, we'll get called back on StreamCreated().
|
||||
audio_ = plugin_delegate->CreateAudio(config_->sample_rate(),
|
||||
config_->sample_frame_count(),
|
||||
this);
|
||||
return audio_ != NULL;
|
||||
}
|
||||
|
||||
int32_t Audio::Open(PluginDelegate* plugin_delegate,
|
||||
PP_Resource config_id,
|
||||
PP_CompletionCallback create_callback) {
|
||||
DCHECK(!audio_);
|
||||
config_ = Resource::GetAs<AudioConfig>(config_id);
|
||||
if (!config_)
|
||||
return PP_ERROR_BADRESOURCE;
|
||||
|
||||
// When the stream is created, we'll get called back on StreamCreated().
|
||||
audio_ = plugin_delegate->CreateAudio(config_->sample_rate(),
|
||||
config_->sample_frame_count(),
|
||||
this);
|
||||
if (!audio_)
|
||||
return PP_ERROR_FAILED;
|
||||
|
||||
// At this point, we are guaranteeing ownership of the completion
|
||||
// callback. Audio promises to fire the completion callback
|
||||
// once and only once.
|
||||
create_callback_ = create_callback;
|
||||
create_callback_pending_ = true;
|
||||
return PP_ERROR_WOULDBLOCK;
|
||||
}
|
||||
|
||||
int32_t Audio::GetSyncSocket(int* sync_socket) {
|
||||
if (socket_ != NULL) {
|
||||
#if defined(OS_POSIX)
|
||||
*sync_socket = socket_->handle();
|
||||
#elif defined(OS_WIN)
|
||||
*sync_socket = reinterpret_cast<int>(socket_->handle());
|
||||
#else
|
||||
#error "Platform not supported."
|
||||
#endif
|
||||
return PP_OK;
|
||||
}
|
||||
return PP_ERROR_FAILED;
|
||||
}
|
||||
|
||||
int32_t Audio::GetSharedMemory(int* shm_handle, int32_t* shm_size) {
|
||||
if (shared_memory_ != NULL) {
|
||||
#if defined(OS_POSIX)
|
||||
*shm_handle = shared_memory_->handle().fd;
|
||||
#elif defined(OS_WIN)
|
||||
*shm_handle = reinterpret_cast<int>(shared_memory_->handle());
|
||||
#else
|
||||
#error "Platform not supported."
|
||||
#endif
|
||||
*shm_size = shared_memory_size_;
|
||||
return PP_OK;
|
||||
}
|
||||
return PP_ERROR_FAILED;
|
||||
}
|
||||
|
||||
bool Audio::StartPlayback() {
|
||||
@ -250,8 +351,17 @@ void Audio::StreamCreated(base::SharedMemoryHandle shared_memory_handle,
|
||||
shared_memory_.reset(new base::SharedMemory(shared_memory_handle, false));
|
||||
shared_memory_size_ = shared_memory_size;
|
||||
|
||||
// Trusted side of proxy can specify a callback to recieve handles.
|
||||
if (create_callback_pending_) {
|
||||
PP_RunCompletionCallback(&create_callback_, 0);
|
||||
create_callback_pending_ = false;
|
||||
}
|
||||
|
||||
// Trusted, non-proxy audio will invoke buffer filling callback on a
|
||||
// dedicated thread, see Audio::Run() below.
|
||||
if (callback_) {
|
||||
shared_memory_->Map(shared_memory_size_);
|
||||
|
||||
// In common case StartPlayback() was called before StreamCreated().
|
||||
if (playing_) {
|
||||
audio_thread_.reset(new base::DelegateSimpleThread(this,
|
||||
@ -277,4 +387,3 @@ void Audio::Run() {
|
||||
}
|
||||
|
||||
} // namespace pepper
|
||||
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "ppapi/c/dev/ppb_audio_dev.h"
|
||||
#include "ppapi/c/dev/ppb_audio_config_dev.h"
|
||||
#include "ppapi/c/dev/ppb_audio_trusted_dev.h"
|
||||
#include "ppapi/c/pp_completion_callback.h"
|
||||
#include "webkit/glue/plugins/pepper_plugin_delegate.h"
|
||||
#include "webkit/glue/plugins/pepper_plugin_instance.h"
|
||||
#include "webkit/glue/plugins/pepper_plugin_module.h"
|
||||
@ -46,19 +47,32 @@ class Audio : public Resource,
|
||||
public PluginDelegate::PlatformAudio::Client,
|
||||
public base::DelegateSimpleThread::Delegate {
|
||||
public:
|
||||
explicit Audio(PluginModule* module);
|
||||
explicit Audio(PluginModule* module, PP_Instance instance_id);
|
||||
virtual ~Audio();
|
||||
|
||||
static const PPB_Audio_Dev* GetInterface();
|
||||
static const PPB_AudioTrusted_Dev* GetTrustedInterface();
|
||||
|
||||
bool Init(PluginDelegate* plugin_delegate, PP_Resource config_id,
|
||||
PPB_Audio_Callback callback, void* user_data);
|
||||
bool Init(PluginDelegate* plugin_delegate,
|
||||
PP_Resource config_id,
|
||||
PPB_Audio_Callback user_callback, void* user_data);
|
||||
|
||||
int32_t Open(PluginDelegate* plugin_delegate,
|
||||
PP_Resource config_id,
|
||||
PP_CompletionCallback create_callback);
|
||||
|
||||
PP_Resource GetCurrentConfiguration() {
|
||||
return config_->GetReference();
|
||||
}
|
||||
|
||||
PP_Instance pp_instance() {
|
||||
return pp_instance_;
|
||||
}
|
||||
|
||||
int32_t GetSyncSocket(int* sync_socket);
|
||||
|
||||
int32_t GetSharedMemory(int* shm_handle, int32_t* shm_size);
|
||||
|
||||
bool StartPlayback();
|
||||
|
||||
bool StopPlayback();
|
||||
@ -83,8 +97,11 @@ class Audio : public Resource,
|
||||
// AudioConfig used for creating this Audio object.
|
||||
scoped_refptr<AudioConfig> config_;
|
||||
|
||||
// Instance id
|
||||
PP_Instance pp_instance_;
|
||||
|
||||
// PluginDelegate audio object that we delegate audio IPC through.
|
||||
scoped_ptr<PluginDelegate::PlatformAudio> audio_;
|
||||
PluginDelegate::PlatformAudio* audio_;
|
||||
|
||||
// Socket used to notify us when audio is ready to accept new samples. This
|
||||
// pointer is created in StreamCreated().
|
||||
@ -106,9 +123,14 @@ class Audio : public Resource,
|
||||
|
||||
// User data pointer passed verbatim to the callback function.
|
||||
void* user_data_;
|
||||
|
||||
// Is a create callback pending to fire?
|
||||
bool create_callback_pending_;
|
||||
|
||||
// Trusted callback invoked from StreamCreated.
|
||||
PP_CompletionCallback create_callback_;
|
||||
};
|
||||
|
||||
} // namespace pepper
|
||||
|
||||
#endif // WEBKIT_GLUE_PLUGINS_PEPPER_DEVICE_CONTEXT_AUDIO_H_
|
||||
|
||||
|
@ -129,8 +129,6 @@ class PluginDelegate {
|
||||
base::SyncSocket::Handle socket) = 0;
|
||||
};
|
||||
|
||||
virtual ~PlatformAudio() {}
|
||||
|
||||
// Starts the playback. Returns false on error or if called before the
|
||||
// stream is created or after the stream is closed.
|
||||
virtual bool StartPlayback() = 0;
|
||||
@ -142,6 +140,9 @@ class PluginDelegate {
|
||||
// Closes the stream. Make sure to call this before the object is
|
||||
// destructed.
|
||||
virtual void ShutDown() = 0;
|
||||
|
||||
protected:
|
||||
virtual ~PlatformAudio() {}
|
||||
};
|
||||
|
||||
class PlatformVideoDecoder {
|
||||
|
Reference in New Issue
Block a user