diff options
author | yzshen@chromium.org <yzshen@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-03-07 07:48:48 +0000 |
---|---|---|
committer | yzshen@chromium.org <yzshen@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-03-07 07:48:48 +0000 |
commit | eed24562b1e6a431fc726195e1114eed01b1694f (patch) | |
tree | 2ae570b6f55f7043e62bc25dca335d03ba75bb52 /ppapi | |
parent | e974ad29d8f31b1f86bb551dc99ad4e5e62b002c (diff) | |
download | chromium_src-eed24562b1e6a431fc726195e1114eed01b1694f.zip chromium_src-eed24562b1e6a431fc726195e1114eed01b1694f.tar.gz chromium_src-eed24562b1e6a431fc726195e1114eed01b1694f.tar.bz2 |
PPB_AudioInput_Dev: support multiple audio input devices - Part 1.
- This CL implements PPB_AudioInput_Dev v0.2 and extends examples/audio_input.
- This CL doesn't actually open devices other than the default one. That will be in a separate CL.
BUG=None
TEST=examples/audio_input
Review URL: http://codereview.chromium.org/9557007
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@125362 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ppapi')
-rw-r--r-- | ppapi/api/dev/ppb_audio_input_dev.idl | 135 | ||||
-rw-r--r-- | ppapi/c/dev/ppb_audio_input_dev.h | 137 | ||||
-rw-r--r-- | ppapi/cpp/dev/audio_input_dev.cc | 212 | ||||
-rw-r--r-- | ppapi/cpp/dev/audio_input_dev.h | 66 | ||||
-rw-r--r-- | ppapi/examples/audio_input/audio_input.cc | 107 | ||||
-rw-r--r-- | ppapi/examples/audio_input/audio_input.html | 91 | ||||
-rw-r--r-- | ppapi/proxy/ppapi_messages.h | 25 | ||||
-rw-r--r-- | ppapi/proxy/ppb_audio_input_proxy.cc | 294 | ||||
-rw-r--r-- | ppapi/proxy/ppb_audio_input_proxy.h | 47 | ||||
-rw-r--r-- | ppapi/proxy/resource_creation_proxy.cc | 13 | ||||
-rw-r--r-- | ppapi/proxy/resource_creation_proxy.h | 4 | ||||
-rw-r--r-- | ppapi/shared_impl/ppb_audio_input_shared.cc | 189 | ||||
-rw-r--r-- | ppapi/shared_impl/ppb_audio_input_shared.h | 111 | ||||
-rw-r--r-- | ppapi/thunk/interfaces_ppb_public_dev.h | 2 | ||||
-rw-r--r-- | ppapi/thunk/ppb_audio_input_api.h | 20 | ||||
-rw-r--r-- | ppapi/thunk/ppb_audio_input_thunk.cc | 91 | ||||
-rw-r--r-- | ppapi/thunk/ppb_audio_input_trusted_thunk.cc | 4 | ||||
-rw-r--r-- | ppapi/thunk/resource_creation_api.h | 4 |
18 files changed, 1262 insertions, 290 deletions
diff --git a/ppapi/api/dev/ppb_audio_input_dev.idl b/ppapi/api/dev/ppb_audio_input_dev.idl index 1411d85..fe2823f 100644 --- a/ppapi/api/dev/ppb_audio_input_dev.idl +++ b/ppapi/api/dev/ppb_audio_input_dev.idl @@ -9,7 +9,8 @@ */ label Chrome { - M17 = 0.1 + M17 = 0.1, + M19 = 0.2 }; /** @@ -25,12 +26,9 @@ typedef void PPB_AudioInput_Callback([in] mem_t sample_buffer, * The <code>PPB_AudioInput_Dev</code> interface contains pointers to several * functions for handling audio input resources. */ -[version=0.1, macro="PPB_AUDIO_INPUT_DEV_INTERFACE"] +[macro="PPB_AUDIO_INPUT_DEV_INTERFACE"] interface PPB_AudioInput_Dev { - /** - * Create is a pointer to a function that creates an audio input resource. - * No sound will be captured until StartCapture() is called. - */ + [deprecate=0.2] PP_Resource Create( [in] PP_Instance instance, [in] PP_Resource config, @@ -38,23 +36,90 @@ interface PPB_AudioInput_Dev { [inout] mem_t user_data); /** - * IsAudioInput is a pointer to a function that determines if the given - * resource is an audio input resource. + * Creates an audio input resource. * - * @param[in] resource A PP_Resource containing a resource. + * @param[in] instance A <code>PP_Instance</code> identifying one instance of + * a module. * - * @return A PP_BOOL containing containing PP_TRUE if the given resource is - * an audio input resource, otherwise PP_FALSE. + * @return A <code>PP_Resource</code> corresponding to an audio input resource + * if successful, 0 if failed. + */ + [version=0.2] + PP_Resource Create( + [in] PP_Instance instance); + + /** + * Determines if the given resource is an audio input resource. + * + * @param[in] resource A <code>PP_Resource</code> containing a resource. + * + * @return A <code>PP_Bool</code> containing <code>PP_TRUE</code> if the given + * resource is an audio input resource, otherwise <code>PP_FALSE</code>. */ PP_Bool IsAudioInput( - [in] PP_Resource audio_input); + [in] PP_Resource resource); /** - * GetCurrrentConfig() returns an audio config resource for the given audio - * resource. + * Enumerates audio input devices. + * + * Please note that: + * - this method ignores the previous value pointed to by <code>devices</code> + * (won't release reference even if it is not 0); + * - <code>devices</code> must be valid until <code>callback</code> is called, + * if the method returns <code>PP_OK_COMPLETIONPENDING</code>; + * - the ref count of the returned <code>devices</code> has already been + * increased by 1 for the caller. + * + * @param[in] audio_input A <code>PP_Resource</code> corresponding to an audio + * input resource. + * @param[out] devices Once the operation is completed successfully, + * <code>devices</code> will be set to a <code>PPB_ResourceArray_Dev</code> + * resource, which holds a list of <code>PPB_DeviceRef_Dev</code> resources. + * @param[in] callback A <code>PP_CompletionCallback</code> to run on + * completion. + * + * @return An error code from <code>pp_errors.h</code>. + */ + [version=0.2] + int32_t EnumerateDevices( + [in] PP_Resource audio_input, + [out] PP_Resource devices, + [in] PP_CompletionCallback callback); + + /** + * Opens an audio input device. No sound will be captured until + * StartCapture() is called. * - * @param[in] config A <code>PP_Resource</code> corresponding to an audio + * @param[in] audio_input A <code>PP_Resource</code> corresponding to an audio + * input resource. + * @param[in] device_ref Identifies an audio input device. It could be one of + * the resource in the array returned by EnumerateDevices(), or 0 which means + * the default device. + * @param[in] config A <code>PPB_AudioConfig</code> audio configuration * resource. + * @param[in] audio_input_callback A <code>PPB_AudioInput_Callback</code> + * function that will be called when data is available. + * @param[inout] user_data An opaque pointer that will be passed into + * <code>audio_input_callback</code>. + * @param[in] callback A <code>PP_CompletionCallback</code> to run when this + * open operation is completed. + * + * @return An error code from <code>pp_errors.h</code>. + */ + [version=0.2] + int32_t Open( + [in] PP_Resource audio_input, + [in] PP_Resource device_ref, + [in] PP_Resource config, + [in] PPB_AudioInput_Callback audio_input_callback, + [inout] mem_t user_data, + [in] PP_CompletionCallback callback); + + /** + * Returns an audio config resource for the given audio input resource. + * + * @param[in] audio_input A <code>PP_Resource</code> corresponding to an audio + * input resource. * * @return A <code>PP_Resource</code> containing the audio config resource if * successful. @@ -63,32 +128,44 @@ interface PPB_AudioInput_Dev { [in] PP_Resource audio_input); /** - * StartCapture() starts the capture of the audio input resource and begins - * periodically calling the callback. + * Starts the capture of the audio input resource and begins periodically + * calling the callback. * - * @param[in] config A <code>PP_Resource</code> corresponding to an audio + * @param[in] audio_input A <code>PP_Resource</code> corresponding to an audio * input resource. * * @return A <code>PP_Bool</code> containing <code>PP_TRUE</code> if - * successful, otherwise <code>PP_FALSE</code>. Also returns - * <code>PP_TRUE</code> (and be a no-op) if called while callback is already - * in progress. + * successful, otherwise <code>PP_FALSE</code>. + * Also returns <code>PP_TRUE</code> (and is a no-op) if called while capture + * is already started. */ PP_Bool StartCapture( [in] PP_Resource audio_input); /** - * StopCapture is a pointer to a function that stops the capture of - * the audio input resource. + * Stops the capture of the audio input resource. * - * @param[in] config A PP_Resource containing the audio input resource. + * @param[in] audio_input A PP_Resource containing the audio input resource. * - * @return A PP_BOOL containing PP_TRUE if successful, otherwise PP_FALSE. - * Also returns PP_TRUE (and is a no-op) if called while capture is already - * stopped. If a buffer is being captured, StopCapture will block until the - * call completes. + * @return A <code>PP_Bool</code> containing <code>PP_TRUE</code> if + * successful, otherwise <code>PP_FALSE</code>. + * Also returns <code>PP_TRUE</code> (and is a no-op) if called while capture + * is already stopped. If a buffer is being captured, StopCapture will block + * until the call completes. */ PP_Bool StopCapture( [in] PP_Resource audio_input); -}; + /** + * Closes the audio input device, and stops capturing if necessary. It is + * not valid to call Open() again after a call to this method. + * If an audio input resource is destroyed while a device is still open, then + * it will be implicitly closed, so you are not required to call this method. + * + * @param[in] audio_input A <code>PP_Resource</code> corresponding to an audio + * input resource. + */ + [version=0.2] + void Close( + [in] PP_Resource audio_input); +}; diff --git a/ppapi/c/dev/ppb_audio_input_dev.h b/ppapi/c/dev/ppb_audio_input_dev.h index 671f51e..32fc928 100644 --- a/ppapi/c/dev/ppb_audio_input_dev.h +++ b/ppapi/c/dev/ppb_audio_input_dev.h @@ -3,19 +3,21 @@ * found in the LICENSE file. */ -/* From dev/ppb_audio_input_dev.idl modified Mon Nov 28 22:30:37 2011. */ +/* From dev/ppb_audio_input_dev.idl modified Sat Mar 3 23:06:35 2012. */ #ifndef PPAPI_C_DEV_PPB_AUDIO_INPUT_DEV_H_ #define PPAPI_C_DEV_PPB_AUDIO_INPUT_DEV_H_ #include "ppapi/c/pp_bool.h" +#include "ppapi/c/pp_completion_callback.h" #include "ppapi/c/pp_instance.h" #include "ppapi/c/pp_macros.h" #include "ppapi/c/pp_resource.h" #include "ppapi/c/pp_stdint.h" #define PPB_AUDIO_INPUT_DEV_INTERFACE_0_1 "PPB_AudioInput(Dev);0.1" -#define PPB_AUDIO_INPUT_DEV_INTERFACE PPB_AUDIO_INPUT_DEV_INTERFACE_0_1 +#define PPB_AUDIO_INPUT_DEV_INTERFACE_0_2 "PPB_AudioInput(Dev);0.2" +#define PPB_AUDIO_INPUT_DEV_INTERFACE PPB_AUDIO_INPUT_DEV_INTERFACE_0_2 /** * @file @@ -48,64 +50,135 @@ typedef void (*PPB_AudioInput_Callback)(const void* sample_buffer, * The <code>PPB_AudioInput_Dev</code> interface contains pointers to several * functions for handling audio input resources. */ -struct PPB_AudioInput_Dev_0_1 { +struct PPB_AudioInput_Dev_0_2 { /** - * Create is a pointer to a function that creates an audio input resource. - * No sound will be captured until StartCapture() is called. + * Creates an audio input resource. + * + * @param[in] instance A <code>PP_Instance</code> identifying one instance of + * a module. + * + * @return A <code>PP_Resource</code> corresponding to an audio input resource + * if successful, 0 if failed. */ - PP_Resource (*Create)(PP_Instance instance, - PP_Resource config, - PPB_AudioInput_Callback audio_input_callback, - void* user_data); + PP_Resource (*Create)(PP_Instance instance); /** - * IsAudioInput is a pointer to a function that determines if the given - * resource is an audio input resource. + * Determines if the given resource is an audio input resource. * - * @param[in] resource A PP_Resource containing a resource. + * @param[in] resource A <code>PP_Resource</code> containing a resource. * - * @return A PP_BOOL containing containing PP_TRUE if the given resource is - * an audio input resource, otherwise PP_FALSE. + * @return A <code>PP_Bool</code> containing <code>PP_TRUE</code> if the given + * resource is an audio input resource, otherwise <code>PP_FALSE</code>. */ - PP_Bool (*IsAudioInput)(PP_Resource audio_input); + PP_Bool (*IsAudioInput)(PP_Resource resource); /** - * GetCurrrentConfig() returns an audio config resource for the given audio - * resource. + * Enumerates audio input devices. + * + * Please note that: + * - this method ignores the previous value pointed to by <code>devices</code> + * (won't release reference even if it is not 0); + * - <code>devices</code> must be valid until <code>callback</code> is called, + * if the method returns <code>PP_OK_COMPLETIONPENDING</code>; + * - the ref count of the returned <code>devices</code> has already been + * increased by 1 for the caller. * - * @param[in] config A <code>PP_Resource</code> corresponding to an audio + * @param[in] audio_input A <code>PP_Resource</code> corresponding to an audio + * input resource. + * @param[out] devices Once the operation is completed successfully, + * <code>devices</code> will be set to a <code>PPB_ResourceArray_Dev</code> + * resource, which holds a list of <code>PPB_DeviceRef_Dev</code> resources. + * @param[in] callback A <code>PP_CompletionCallback</code> to run on + * completion. + * + * @return An error code from <code>pp_errors.h</code>. + */ + int32_t (*EnumerateDevices)(PP_Resource audio_input, + PP_Resource* devices, + struct PP_CompletionCallback callback); + /** + * Opens an audio input device. No sound will be captured until + * StartCapture() is called. + * + * @param[in] audio_input A <code>PP_Resource</code> corresponding to an audio + * input resource. + * @param[in] device_ref Identifies an audio input device. It could be one of + * the resource in the array returned by EnumerateDevices(), or 0 which means + * the default device. + * @param[in] config A <code>PPB_AudioConfig</code> audio configuration * resource. + * @param[in] audio_input_callback A <code>PPB_AudioInput_Callback</code> + * function that will be called when data is available. + * @param[inout] user_data An opaque pointer that will be passed into + * <code>audio_input_callback</code>. + * @param[in] callback A <code>PP_CompletionCallback</code> to run when this + * open operation is completed. + * + * @return An error code from <code>pp_errors.h</code>. + */ + int32_t (*Open)(PP_Resource audio_input, + PP_Resource device_ref, + PP_Resource config, + PPB_AudioInput_Callback audio_input_callback, + void* user_data, + struct PP_CompletionCallback callback); + /** + * Returns an audio config resource for the given audio input resource. + * + * @param[in] audio_input A <code>PP_Resource</code> corresponding to an audio + * input resource. * * @return A <code>PP_Resource</code> containing the audio config resource if * successful. */ PP_Resource (*GetCurrentConfig)(PP_Resource audio_input); /** - * StartCapture() starts the capture of the audio input resource and begins - * periodically calling the callback. + * Starts the capture of the audio input resource and begins periodically + * calling the callback. * - * @param[in] config A <code>PP_Resource</code> corresponding to an audio + * @param[in] audio_input A <code>PP_Resource</code> corresponding to an audio * input resource. * * @return A <code>PP_Bool</code> containing <code>PP_TRUE</code> if - * successful, otherwise <code>PP_FALSE</code>. Also returns - * <code>PP_TRUE</code> (and be a no-op) if called while callback is already - * in progress. + * successful, otherwise <code>PP_FALSE</code>. + * Also returns <code>PP_TRUE</code> (and is a no-op) if called while capture + * is already started. */ PP_Bool (*StartCapture)(PP_Resource audio_input); /** - * StopCapture is a pointer to a function that stops the capture of - * the audio input resource. + * Stops the capture of the audio input resource. * - * @param[in] config A PP_Resource containing the audio input resource. + * @param[in] audio_input A PP_Resource containing the audio input resource. * - * @return A PP_BOOL containing PP_TRUE if successful, otherwise PP_FALSE. - * Also returns PP_TRUE (and is a no-op) if called while capture is already - * stopped. If a buffer is being captured, StopCapture will block until the - * call completes. + * @return A <code>PP_Bool</code> containing <code>PP_TRUE</code> if + * successful, otherwise <code>PP_FALSE</code>. + * Also returns <code>PP_TRUE</code> (and is a no-op) if called while capture + * is already stopped. If a buffer is being captured, StopCapture will block + * until the call completes. */ PP_Bool (*StopCapture)(PP_Resource audio_input); + /** + * Closes the audio input device, and stops capturing if necessary. It is + * not valid to call Open() again after a call to this method. + * If an audio input resource is destroyed while a device is still open, then + * it will be implicitly closed, so you are not required to call this method. + * + * @param[in] audio_input A <code>PP_Resource</code> corresponding to an audio + * input resource. + */ + void (*Close)(PP_Resource audio_input); }; -typedef struct PPB_AudioInput_Dev_0_1 PPB_AudioInput_Dev; +typedef struct PPB_AudioInput_Dev_0_2 PPB_AudioInput_Dev; + +struct PPB_AudioInput_Dev_0_1 { + PP_Resource (*Create)(PP_Instance instance, + PP_Resource config, + PPB_AudioInput_Callback audio_input_callback, + void* user_data); + PP_Bool (*IsAudioInput)(PP_Resource resource); + PP_Resource (*GetCurrentConfig)(PP_Resource audio_input); + PP_Bool (*StartCapture)(PP_Resource audio_input); + PP_Bool (*StopCapture)(PP_Resource audio_input); +}; /** * @} */ diff --git a/ppapi/cpp/dev/audio_input_dev.cc b/ppapi/cpp/dev/audio_input_dev.cc index 386345b..2429259 100644 --- a/ppapi/cpp/dev/audio_input_dev.cc +++ b/ppapi/cpp/dev/audio_input_dev.cc @@ -1,11 +1,15 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// 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 "ppapi/cpp/dev/audio_input_dev.h" #include "ppapi/c/dev/ppb_audio_input_dev.h" +#include "ppapi/c/pp_bool.h" #include "ppapi/c/pp_errors.h" +#include "ppapi/cpp/completion_callback.h" +#include "ppapi/cpp/dev/device_ref_dev.h" +#include "ppapi/cpp/dev/resource_array_dev.h" #include "ppapi/cpp/instance_handle.h" #include "ppapi/cpp/module.h" #include "ppapi/cpp/module_impl.h" @@ -14,36 +18,222 @@ namespace pp { namespace { -template <> const char* interface_name<PPB_AudioInput_Dev>() { - return PPB_AUDIO_INPUT_DEV_INTERFACE; +template <> const char* interface_name<PPB_AudioInput_Dev_0_2>() { + return PPB_AUDIO_INPUT_DEV_INTERFACE_0_2; +} + +template <> const char* interface_name<PPB_AudioInput_Dev_0_1>() { + return PPB_AUDIO_INPUT_DEV_INTERFACE_0_1; } } // namespace +struct AudioInput_Dev::EnumerateDevicesState { + EnumerateDevicesState(std::vector<DeviceRef_Dev>* in_devices, + const CompletionCallback& in_callback, + AudioInput_Dev* in_audio_input) + : devices_resource(0), + devices(in_devices), + callback(in_callback), + audio_input(in_audio_input) { + } + + PP_Resource devices_resource; + // This is owned by the user of AudioInput_Dev::EnumerateDevices(). + std::vector<DeviceRef_Dev>* devices; + CompletionCallback callback; + AudioInput_Dev* audio_input; +}; + +AudioInput_Dev::AudioInput_Dev() : enum_state_(NULL), + audio_input_callback_(NULL), + user_data_(NULL) { +} + AudioInput_Dev::AudioInput_Dev(const InstanceHandle& instance, const AudioConfig& config, PPB_AudioInput_Callback callback, void* user_data) - : config_(config) { - if (has_interface<PPB_AudioInput_Dev>()) { - PassRefFromConstructor(get_interface<PPB_AudioInput_Dev>()->Create( + : config_(config), + enum_state_(NULL), + audio_input_callback_(callback), + user_data_(user_data) { + if (has_interface<PPB_AudioInput_Dev_0_2>()) { + PassRefFromConstructor(get_interface<PPB_AudioInput_Dev_0_2>()->Create( + instance.pp_instance())); + } else if (has_interface<PPB_AudioInput_Dev_0_1>()) { + PassRefFromConstructor(get_interface<PPB_AudioInput_Dev_0_1>()->Create( instance.pp_instance(), config.pp_resource(), callback, user_data)); } } +AudioInput_Dev::AudioInput_Dev(const InstanceHandle& instance) + : enum_state_(NULL), + audio_input_callback_(NULL), + user_data_(NULL) { + if (has_interface<PPB_AudioInput_Dev_0_2>()) { + PassRefFromConstructor(get_interface<PPB_AudioInput_Dev_0_2>()->Create( + instance.pp_instance())); + } +} + +AudioInput_Dev::AudioInput_Dev(const AudioInput_Dev& other) + : Resource(other), + config_(other.config_), + enum_state_(NULL), + audio_input_callback_(other.audio_input_callback_), + user_data_(other.user_data_) { +} + +AudioInput_Dev::~AudioInput_Dev() { + AbortEnumerateDevices(); +} + +AudioInput_Dev& AudioInput_Dev::operator=(const AudioInput_Dev& other) { + AbortEnumerateDevices(); + + Resource::operator=(other); + config_ = other.config_; + audio_input_callback_ = other.audio_input_callback_; + user_data_ = other.user_data_; + return *this; +} + // static bool AudioInput_Dev::IsAvailable() { - return has_interface<PPB_AudioInput_Dev>(); + return has_interface<PPB_AudioInput_Dev_0_2>() || + has_interface<PPB_AudioInput_Dev_0_1>(); +} + +int32_t AudioInput_Dev::EnumerateDevices(std::vector<DeviceRef_Dev>* devices, + const CompletionCallback& callback) { + if (!has_interface<PPB_AudioInput_Dev_0_2>()) + return callback.MayForce(PP_ERROR_NOINTERFACE); + if (!devices) + return callback.MayForce(PP_ERROR_BADARGUMENT); + if (!callback.pp_completion_callback().func) + return callback.MayForce(PP_ERROR_BLOCKS_MAIN_THREAD); + if (enum_state_) + return callback.MayForce(PP_ERROR_INPROGRESS); + + // It will be deleted in OnEnumerateDevicesComplete(). + enum_state_ = new EnumerateDevicesState(devices, callback, this); + return get_interface<PPB_AudioInput_Dev_0_2>()->EnumerateDevices( + pp_resource(), &enum_state_->devices_resource, + PP_MakeCompletionCallback(&AudioInput_Dev::OnEnumerateDevicesComplete, + enum_state_)); +} + +int32_t AudioInput_Dev::Open(const DeviceRef_Dev& device_ref, + const CompletionCallback& callback) { + if (has_interface<PPB_AudioInput_Dev_0_2>()) { + return get_interface<PPB_AudioInput_Dev_0_2>()->Open( + pp_resource(), device_ref.pp_resource(), config_.pp_resource(), + audio_input_callback_, user_data_, callback.pp_completion_callback()); + } + + if (has_interface<PPB_AudioInput_Dev_0_1>()) { + if (is_null()) + return callback.MayForce(PP_ERROR_FAILED); + + // If the v0.1 interface is being used and there is a valid resource handle, + // then the default device has been successfully opened during resource + // creation. + if (device_ref.is_null()) + return callback.MayForce(PP_OK); + + // The v0.1 interface doesn't support devices other than the default one. + return callback.MayForce(PP_ERROR_NOTSUPPORTED); + } + + return callback.MayForce(PP_ERROR_NOINTERFACE); +} + +int32_t AudioInput_Dev::Open(const DeviceRef_Dev& device_ref, + const AudioConfig& config, + PPB_AudioInput_Callback audio_input_callback, + void* user_data, + const CompletionCallback& callback) { + if (has_interface<PPB_AudioInput_Dev_0_2>()) { + return get_interface<PPB_AudioInput_Dev_0_2>()->Open( + pp_resource(), device_ref.pp_resource(), config.pp_resource(), + audio_input_callback, user_data, callback.pp_completion_callback()); + } + + return callback.MayForce(PP_ERROR_NOINTERFACE); } bool AudioInput_Dev::StartCapture() { - return has_interface<PPB_AudioInput_Dev>() && - get_interface<PPB_AudioInput_Dev>()->StartCapture(pp_resource()); + if (has_interface<PPB_AudioInput_Dev_0_2>()) { + return PP_ToBool(get_interface<PPB_AudioInput_Dev_0_2>()->StartCapture( + pp_resource())); + } + + if (has_interface<PPB_AudioInput_Dev_0_1>()) { + return PP_ToBool(get_interface<PPB_AudioInput_Dev_0_1>()->StartCapture( + pp_resource())); + } + + return false; } bool AudioInput_Dev::StopCapture() { - return has_interface<PPB_AudioInput_Dev>() && - get_interface<PPB_AudioInput_Dev>()->StopCapture(pp_resource()); + if (has_interface<PPB_AudioInput_Dev_0_2>()) { + return PP_ToBool(get_interface<PPB_AudioInput_Dev_0_2>()->StopCapture( + pp_resource())); + } + + if (has_interface<PPB_AudioInput_Dev_0_1>()) { + return PP_ToBool(get_interface<PPB_AudioInput_Dev_0_1>()->StopCapture( + pp_resource())); + } + + return false; +} + +void AudioInput_Dev::Close() { + if (has_interface<PPB_AudioInput_Dev_0_2>()) + get_interface<PPB_AudioInput_Dev_0_2>()->Close(pp_resource()); +} + +void AudioInput_Dev::AbortEnumerateDevices() { + if (enum_state_) { + enum_state_->devices = NULL; + Module::Get()->core()->CallOnMainThread(0, enum_state_->callback, + PP_ERROR_ABORTED); + enum_state_->audio_input = NULL; + enum_state_ = NULL; + } +} + +// static +void AudioInput_Dev::OnEnumerateDevicesComplete(void* user_data, + int32_t result) { + EnumerateDevicesState* enum_state = + static_cast<EnumerateDevicesState*>(user_data); + + bool need_to_callback = !!enum_state->audio_input; + + if (result == PP_OK) { + // It will take care of releasing the reference. + ResourceArray_Dev resources(pp::PASS_REF, + enum_state->devices_resource); + + if (need_to_callback) { + enum_state->devices->clear(); + for (uint32_t index = 0; index < resources.size(); ++index) { + DeviceRef_Dev device(resources[index]); + enum_state->devices->push_back(device); + } + } + } + + if (need_to_callback) { + enum_state->audio_input->enum_state_ = NULL; + enum_state->callback.Run(result); + } + + delete enum_state; } } // namespace pp diff --git a/ppapi/cpp/dev/audio_input_dev.h b/ppapi/cpp/dev/audio_input_dev.h index 2761437..2bcccc3 100644 --- a/ppapi/cpp/dev/audio_input_dev.h +++ b/ppapi/cpp/dev/audio_input_dev.h @@ -1,28 +1,49 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// 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. #ifndef PPAPI_CPP_DEV_AUDIO_INPUT_DEV_H_ #define PPAPI_CPP_DEV_AUDIO_INPUT_DEV_H_ +#include <vector> + #include "ppapi/c/dev/ppb_audio_input_dev.h" #include "ppapi/cpp/audio_config.h" #include "ppapi/cpp/resource.h" namespace pp { +class CompletionCallback; +class DeviceRef_Dev; class InstanceHandle; class AudioInput_Dev : public Resource { public: /// An empty constructor for an AudioInput resource. - AudioInput_Dev() {} + AudioInput_Dev(); + /// This constructor tries to create an audio input resource using the v0.2 + /// interface, and falls back on the v0.1 interface if that is not available. + /// Please use the 2-parameter Open() if you used this constructor. + /// + /// Note: This constructor is deprecated. Unless your code has to deal with + /// browsers that only support the v0.1 interface, please use the 1-parameter + /// constructor instead. AudioInput_Dev(const InstanceHandle& instance, const AudioConfig& config, PPB_AudioInput_Callback callback, void* user_data); + /// This constructor uses the v0.2 interface to create an audio input + /// resource. Please use the 5-parameter Open() if you used this constructor. + explicit AudioInput_Dev(const InstanceHandle& instance); + + AudioInput_Dev(const AudioInput_Dev& other); + + virtual ~AudioInput_Dev(); + + AudioInput_Dev& operator=(const AudioInput_Dev& other); + /// Static function for determining whether the browser supports the required /// AudioInput interface. /// @@ -42,11 +63,52 @@ class AudioInput_Dev : public Resource { /// struct. const AudioConfig& config() const { return config_; } + /// |devices| must stay alive until either this AudioInput_Dev object goes + /// away or |callback| is run, if this method returns PP_OK_COMPLETIONPENDING. + int32_t EnumerateDevices(std::vector<DeviceRef_Dev>* devices, + const CompletionCallback& callback); + + /// If |device_ref| is null (i.e., is_null() returns true), the default device + /// will be used. + /// In order to maintain backward compatibility, this method doesn't have + /// input parameters config, audio_input_callback and user_data. Instead, it + /// uses those values stored when the 4-parameter constructor was called. + /// + /// Note: This method is deprecated. Unless your code has to deal with + /// browsers that only support the v0.1 interface, please use the other + /// Open(). + int32_t Open(const DeviceRef_Dev& device_ref, + const CompletionCallback& callback); + + int32_t Open(const DeviceRef_Dev& device_ref, + const AudioConfig& config, + PPB_AudioInput_Callback audio_input_callback, + void* user_data, + const CompletionCallback& callback); + bool StartCapture(); bool StopCapture(); + void Close(); private: + struct EnumerateDevicesState; + + void AbortEnumerateDevices(); + + // |user_data| is an EnumerateDevicesState object. It is this method's + // responsibility to delete it. + static void OnEnumerateDevicesComplete(void* user_data, int32_t result); + AudioConfig config_; + + // It is set in EnumerateDevices(), and cleared in AbortEnumerateDevices() or + // OnEnumerateDevicesComplete(). The object will be deleted by + // OnEnumerateDevicesComplete(). + EnumerateDevicesState* enum_state_; + + // Used to store the arguments of Open() for the v0.2 interface. + PPB_AudioInput_Callback audio_input_callback_; + void* user_data_; }; } // namespace pp diff --git a/ppapi/examples/audio_input/audio_input.cc b/ppapi/examples/audio_input/audio_input.cc index 203718e..44fd337 100644 --- a/ppapi/examples/audio_input/audio_input.cc +++ b/ppapi/examples/audio_input/audio_input.cc @@ -6,9 +6,11 @@ #include <algorithm> #include <limits> +#include <vector> #include "ppapi/cpp/audio_config.h" #include "ppapi/cpp/dev/audio_input_dev.h" +#include "ppapi/cpp/dev/device_ref_dev.h" #include "ppapi/cpp/graphics_2d.h" #include "ppapi/cpp/image_data.h" #include "ppapi/cpp/instance.h" @@ -28,10 +30,18 @@ class MyInstance : public pp::Instance { samples_(NULL), timer_interval_(0), pending_paint_(false), - waiting_for_flush_completion_(false) { + waiting_for_flush_completion_(false), + audio_input_0_1_(0), + audio_input_interface_0_1_(NULL) { } virtual ~MyInstance() { - audio_input_.StopCapture(); + if (!audio_input_.is_null()) { + audio_input_.Close(); + } else if (audio_input_0_1_ != 0) { + audio_input_interface_0_1_->StopCapture(audio_input_0_1_); + pp::Module::Get()->core()->ReleaseResource(audio_input_0_1_); + } + delete[] samples_; } @@ -52,7 +62,11 @@ class MyInstance : public pp::Instance { samples_ = new int16_t[sample_count_ * channel_count_]; memset(samples_, 0, sample_count_ * channel_count_ * sizeof(int16_t)); audio_input_ = pp::AudioInput_Dev(this, config, CaptureCallback, this); - if (!audio_input_.StartCapture()) + + audio_input_interface_0_1_ = static_cast<const PPB_AudioInput_Dev_0_1*>( + pp::Module::Get()->GetBrowserInterface( + PPB_AUDIO_INPUT_DEV_INTERFACE_0_1)); + if (!audio_input_interface_0_1_) return false; // Try to ensure that we pick up a new set of samples between each @@ -75,6 +89,42 @@ class MyInstance : public pp::Instance { Paint(); } + virtual void HandleMessage(const pp::Var& message_data) { + if (message_data.is_string()) { + std::string event = message_data.AsString(); + if (event == "PageInitialized") { + pp::CompletionCallback callback = callback_factory_.NewCallback( + &MyInstance::EnumerateDevicesFinished); + int32_t result = audio_input_.EnumerateDevices(&devices_, callback); + if (result != PP_OK_COMPLETIONPENDING) + PostMessage(pp::Var("EnumerationFailed")); + } else if (event == "UseDefault") { + Open(pp::DeviceRef_Dev()); + } else if (event == "UseDefault(v0.1)") { + audio_input_0_1_ = audio_input_interface_0_1_->Create( + pp_instance(), audio_input_.config().pp_resource(), + CaptureCallback, this); + if (audio_input_0_1_ != 0) { + if (!audio_input_interface_0_1_->StartCapture(audio_input_0_1_)) + PostMessage(pp::Var("StartFailed")); + } else { + PostMessage(pp::Var("OpenFailed")); + } + + audio_input_ = pp::AudioInput_Dev(); + } else if (event == "Stop") { + Stop(); + } + } else if (message_data.is_number()) { + int index = message_data.AsInt(); + if (index >= 0 && index < static_cast<int>(devices_.size())) { + Open(devices_[index]); + } else { + PP_NOTREACHED(); + } + } + } + private: void ScheduleNextTimer() { PP_DCHECK(timer_interval_ > 0); @@ -161,6 +211,52 @@ class MyInstance : public pp::Instance { memcpy(thiz->samples_, samples, num_bytes); } + void Open(const pp::DeviceRef_Dev& device) { + pp::CompletionCallback callback = callback_factory_.NewCallback( + &MyInstance::OpenFinished); + int32_t result = audio_input_.Open(device, callback); + if (result != PP_OK_COMPLETIONPENDING) + PostMessage(pp::Var("OpenFailed")); + } + + void Stop() { + if (!audio_input_.is_null()) { + if (!audio_input_.StopCapture()) + PostMessage(pp::Var("StopFailed")); + } else if (audio_input_0_1_ != 0) { + if (!audio_input_interface_0_1_->StopCapture(audio_input_0_1_)) + PostMessage(pp::Var("StopFailed")); + } + } + + void EnumerateDevicesFinished(int32_t result) { + static const char* const kDelimiter = "#__#"; + + if (result == PP_OK) { + std::string device_names; + for (size_t index = 0; index < devices_.size(); ++index) { + pp::Var name = devices_[index].GetName(); + PP_DCHECK(name.is_string()); + + if (index != 0) + device_names += kDelimiter; + device_names += name.AsString(); + } + PostMessage(pp::Var(device_names)); + } else { + PostMessage(pp::Var("EnumerationFailed")); + } + } + + void OpenFinished(int32_t result) { + if (result == PP_OK) { + if (!audio_input_.StartCapture()) + PostMessage(pp::Var("StartFailed")); + } else { + PostMessage(pp::Var("OpenFailed")); + } + } + pp::CompletionCallbackFactory<MyInstance> callback_factory_; uint32_t sample_count_; @@ -176,6 +272,11 @@ class MyInstance : public pp::Instance { bool waiting_for_flush_completion_; pp::AudioInput_Dev audio_input_; + + PP_Resource audio_input_0_1_; + const PPB_AudioInput_Dev_0_1* audio_input_interface_0_1_; + + std::vector<pp::DeviceRef_Dev> devices_; }; class MyModule : public pp::Module { diff --git a/ppapi/examples/audio_input/audio_input.html b/ppapi/examples/audio_input/audio_input.html index bfa91cd..a5256fa 100644 --- a/ppapi/examples/audio_input/audio_input.html +++ b/ppapi/examples/audio_input/audio_input.html @@ -1,18 +1,99 @@ <!DOCTYPE html> <html> <!-- - Copyright (c) 2011 The Chromium Authors. All rights reserved. + 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. --> <head> <title>Audio Input Example</title> -</head> + <script type="text/javascript"> + var device_array = []; -<body> + function HandleMessage(message_event) { + if (message_event.data) { + var status = document.getElementById('status'); + if (message_event.data == 'EnumerationFailed') { + status.innerText = 'Device enumeration failed!'; + } else if (message_event.data == 'OpenFailed') { + status.innerText = 'Open device failed!'; + } else if (message_event.data == 'StartFailed') { + status.innerText = 'Start capturing failed!'; + } else if (message_event.data == 'StopFailed') { + status.innerText = 'Stop capturing failed!'; + } else { + device_array = message_event.data.split('#__#'); + + var list = document.getElementById('device_list'); + for (var i = 0; i < device_array.length; ++i) { + var list_item = document.createElement('li'); + var link = document.createElement('a'); + link.href = 'javascript:UseDesignatedDevice(' + i + ');'; + link.innerText = device_array[i]; + list_item.appendChild(link); + list.appendChild(list_item); + } + } + } + } + + function UseDesignatedDevice(index) { + UseDevice(device_array[index], index); + } + + function UseDefaultDevice(use_0_1_interface) { + var display_text = use_0_1_interface ? 'Default(v0.1)' : 'Default'; + var command = use_0_1_interface ? 'UseDefault(v0.1)' : 'UseDefault'; + UseDevice(display_text, command); + } + + function UseDevice(display_text, command) { + var in_use_device = document.getElementById('in_use_device'); + in_use_device.innerText = display_text; + var plugin = document.getElementById('plugin'); + plugin.postMessage(command); -<embed id="plugin" type="application/x-ppapi-example-audio-input" - width="800" height="400"/> + var available_devices = document.getElementById('available_devices'); + available_devices.parentNode.removeChild(available_devices); + var control_panel = document.getElementById('control_panel'); + var link = document.createElement('a'); + link.href = 'javascript:Stop();'; + link.innerText = 'Stop'; + control_panel.appendChild(link); + } + + function Stop() { + var plugin = document.getElementById('plugin'); + plugin.postMessage('Stop'); + } + + function Initialize() { + var plugin = document.getElementById('plugin'); + plugin.addEventListener('message', HandleMessage, false); + plugin.postMessage('PageInitialized'); + } + + document.addEventListener('DOMContentLoaded', Initialize, false); + </script> +</head> + +<body> + <embed id="plugin" type="application/x-ppapi-example-audio-input" + width="800" height="400"/> + <div style="margin-bottom:10px">In-use device: + <span id="in_use_device" style="font-weight:bold">None</span> + </div> + <div id="available_devices"> + Available device(s), choose one to open: + <ul id="device_list"> + <li><a href="javascript:UseDefaultDevice(true);"> + Default - use interface version 0.1</a></li> + <li><a href="javascript:UseDefaultDevice(false);"> + Default - use interface version 0.2 and NULL device ref</a></li> + </ul> + </div> + <div id="control_panel"></div> + <div id="status"></div> </body> </html> diff --git a/ppapi/proxy/ppapi_messages.h b/ppapi/proxy/ppapi_messages.h index 85b08e3..5860048 100644 --- a/ppapi/proxy/ppapi_messages.h +++ b/ppapi/proxy/ppapi_messages.h @@ -240,8 +240,12 @@ IPC_MESSAGE_ROUTED5(PpapiMsg_PPBAudio_NotifyAudioStreamCreated, int32_t /* length */) // PPB_AudioInput_Dev. -IPC_MESSAGE_ROUTED5(PpapiMsg_PPBAudioInput_NotifyAudioStreamCreated, - ppapi::HostResource /* audio_id */, +IPC_MESSAGE_ROUTED3(PpapiMsg_PPBAudioInput_EnumerateDevicesACK, + ppapi::HostResource /* audio_input */, + int32_t /* result */, + std::vector<ppapi::DeviceRefData> /* devices */) +IPC_MESSAGE_ROUTED5(PpapiMsg_PPBAudioInput_OpenACK, + ppapi::HostResource /* audio_input */, int32_t /* result_code (will be != PP_OK on failure) */, IPC::PlatformFileForTransit /* socket_handle */, base::SharedMemoryHandle /* handle */, @@ -576,14 +580,21 @@ IPC_MESSAGE_ROUTED2(PpapiHostMsg_PPBAudio_StartOrStop, bool /* play */) // PPB_AudioInput. -IPC_SYNC_MESSAGE_ROUTED3_1(PpapiHostMsg_PPBAudioInput_Create, - PP_Instance /* instance_id */, - int32_t /* sample_rate */, - uint32_t /* sample_frame_count */, +IPC_SYNC_MESSAGE_ROUTED1_1(PpapiHostMsg_PPBAudioInput_Create, + PP_Instance /* instance */, ppapi::HostResource /* result */) +IPC_MESSAGE_ROUTED1(PpapiHostMsg_PPBAudioInput_EnumerateDevices, + ppapi::HostResource /* audio_input */) +IPC_MESSAGE_ROUTED4(PpapiHostMsg_PPBAudioInput_Open, + ppapi::HostResource /* audio_input */, + std::string /* device_id */, + int32_t /* sample_rate */, + uint32_t /* sample_frame_count */) IPC_MESSAGE_ROUTED2(PpapiHostMsg_PPBAudioInput_StartOrStop, - ppapi::HostResource /* audio_id */, + ppapi::HostResource /* audio_input */, bool /* capture */) +IPC_MESSAGE_ROUTED1(PpapiHostMsg_PPBAudioInput_Close, + ppapi::HostResource /* audio_input */) // PPB_Broker. IPC_SYNC_MESSAGE_ROUTED1_1(PpapiHostMsg_PPBBroker_Create, diff --git a/ppapi/proxy/ppb_audio_input_proxy.cc b/ppapi/proxy/ppb_audio_input_proxy.cc index d689192..79d6eba 100644 --- a/ppapi/proxy/ppb_audio_input_proxy.cc +++ b/ppapi/proxy/ppb_audio_input_proxy.cc @@ -5,12 +5,9 @@ #include "ppapi/proxy/ppb_audio_input_proxy.h" #include "base/compiler_specific.h" -#include "base/threading/simple_thread.h" #include "ppapi/c/dev/ppb_audio_input_dev.h" #include "ppapi/c/pp_errors.h" #include "ppapi/c/ppb_audio_config.h" -#include "ppapi/c/ppb_var.h" -#include "ppapi/c/trusted/ppb_audio_trusted.h" #include "ppapi/proxy/enter_proxy.h" #include "ppapi/proxy/plugin_dispatcher.h" #include "ppapi/proxy/ppapi_messages.h" @@ -18,104 +15,116 @@ #include "ppapi/shared_impl/platform_file.h" #include "ppapi/shared_impl/ppapi_globals.h" #include "ppapi/shared_impl/ppb_audio_input_shared.h" -#include "ppapi/shared_impl/resource.h" +#include "ppapi/shared_impl/ppb_device_ref_shared.h" +#include "ppapi/shared_impl/tracked_callback.h" #include "ppapi/thunk/enter.h" -#include "ppapi/thunk/ppb_audio_config_api.h" #include "ppapi/thunk/resource_creation_api.h" #include "ppapi/thunk/thunk.h" using ppapi::IntToPlatformFile; -using ppapi::thunk::EnterResourceNoLock; using ppapi::thunk::PPB_AudioInput_API; -using ppapi::thunk::PPB_AudioConfig_API; namespace ppapi { namespace proxy { -class AudioInput : public Resource, public PPB_AudioInput_Shared { +class AudioInput : public PPB_AudioInput_Shared { public: - AudioInput(const HostResource& audio_input_id, - PP_Resource config_id, - PPB_AudioInput_Callback callback, - void* user_data); + explicit AudioInput(const HostResource& audio_input); virtual ~AudioInput(); - // Resource overrides. - virtual PPB_AudioInput_API* AsPPB_AudioInput_API() OVERRIDE; - - // PPB_AudioInput_API implementation. - virtual PP_Resource GetCurrentConfig() OVERRIDE; - virtual PP_Bool StartCapture() OVERRIDE; - virtual PP_Bool StopCapture() OVERRIDE; - - virtual int32_t OpenTrusted(PP_Resource config_id, + // Implementation of PPB_AudioInput_API trusted methods. + virtual int32_t OpenTrusted(const std::string& device_id, + PP_Resource config, PP_CompletionCallback create_callback) OVERRIDE; virtual int32_t GetSyncSocket(int* sync_socket) OVERRIDE; virtual int32_t GetSharedMemory(int* shm_handle, uint32_t* shm_size) OVERRIDE; + virtual const std::vector<DeviceRefData>& GetDeviceRefData() const OVERRIDE; private: - // Owning reference to the current config object. This isn't actually used, - // we just dish it out as requested by the plugin. - PP_Resource config_; + // PPB_AudioInput_Shared implementation. + virtual int32_t InternalEnumerateDevices( + PP_Resource* devices, + PP_CompletionCallback callback) OVERRIDE; + virtual int32_t InternalOpen(const std::string& device_id, + PP_AudioSampleRate sample_rate, + uint32_t sample_frame_count, + PP_CompletionCallback callback) OVERRIDE; + virtual PP_Bool InternalStartCapture() OVERRIDE; + virtual PP_Bool InternalStopCapture() OVERRIDE; + virtual void InternalClose() OVERRIDE; + + PluginDispatcher* GetDispatcher() const { + return PluginDispatcher::GetForResource(this); + } DISALLOW_COPY_AND_ASSIGN(AudioInput); }; -AudioInput::AudioInput(const HostResource& audio_input_id, - PP_Resource config_id, - PPB_AudioInput_Callback callback, - void* user_data) - : Resource(OBJECT_IS_PROXY, audio_input_id), - config_(config_id) { - SetCallback(callback, user_data); - PpapiGlobals::Get()->GetResourceTracker()->AddRefResource(config_); +AudioInput::AudioInput(const HostResource& audio_input) + : PPB_AudioInput_Shared(audio_input) { } AudioInput::~AudioInput() { - PpapiGlobals::Get()->GetResourceTracker()->ReleaseResource(config_); + Close(); } -PPB_AudioInput_API* AudioInput::AsPPB_AudioInput_API() { - return this; +int32_t AudioInput::OpenTrusted(const std::string& device_id, + PP_Resource config, + PP_CompletionCallback create_callback) { + return PP_ERROR_NOTSUPPORTED; // Don't proxy the trusted interface. } -PP_Resource AudioInput::GetCurrentConfig() { - // AddRef for the caller. - PpapiGlobals::Get()->GetResourceTracker()->AddRefResource(config_); - return config_; +int32_t AudioInput::GetSyncSocket(int* sync_socket) { + return PP_ERROR_NOTSUPPORTED; // Don't proxy the trusted interface. } -PP_Bool AudioInput::StartCapture() { - if (capturing()) - return PP_TRUE; - SetStartCaptureState(); - PluginDispatcher::GetForResource(this)->Send( - new PpapiHostMsg_PPBAudioInput_StartOrStop( - API_ID_PPB_AUDIO_INPUT_DEV, host_resource(), true)); - return PP_TRUE; +int32_t AudioInput::GetSharedMemory(int* shm_handle, uint32_t* shm_size) { + return PP_ERROR_NOTSUPPORTED; // Don't proxy the trusted interface. } -PP_Bool AudioInput::StopCapture() { - if (!capturing()) - return PP_TRUE; - PluginDispatcher::GetForResource(this)->Send( - new PpapiHostMsg_PPBAudioInput_StartOrStop( - API_ID_PPB_AUDIO_INPUT_DEV, host_resource(), false)); - SetStopCaptureState(); - return PP_TRUE; +const std::vector<DeviceRefData>& AudioInput::GetDeviceRefData() const { + // Don't proxy the trusted interface. + static std::vector<DeviceRefData> result; + return result; } -int32_t AudioInput::OpenTrusted(PP_Resource config_id, - PP_CompletionCallback create_callback) { - return PP_ERROR_NOTSUPPORTED; // Don't proxy the trusted interface. +int32_t AudioInput::InternalEnumerateDevices(PP_Resource* devices, + PP_CompletionCallback callback) { + devices_ = devices; + enumerate_devices_callback_ = new TrackedCallback(this, callback); + GetDispatcher()->Send(new PpapiHostMsg_PPBAudioInput_EnumerateDevices( + API_ID_PPB_AUDIO_INPUT_DEV, host_resource())); + return PP_OK_COMPLETIONPENDING; } -int32_t AudioInput::GetSyncSocket(int* sync_socket) { - return PP_ERROR_NOTSUPPORTED; // Don't proxy the trusted interface. +int32_t AudioInput::InternalOpen(const std::string& device_id, + PP_AudioSampleRate sample_rate, + uint32_t sample_frame_count, + PP_CompletionCallback callback) { + open_callback_ = new TrackedCallback(this, callback); + GetDispatcher()->Send(new PpapiHostMsg_PPBAudioInput_Open( + API_ID_PPB_AUDIO_INPUT_DEV, host_resource(), device_id, sample_rate, + sample_frame_count)); + return PP_OK_COMPLETIONPENDING; } -int32_t AudioInput::GetSharedMemory(int* shm_handle, uint32_t* shm_size) { - return PP_ERROR_NOTSUPPORTED; // Don't proxy the trusted interface. +PP_Bool AudioInput::InternalStartCapture() { + SetStartCaptureState(); + GetDispatcher()->Send(new PpapiHostMsg_PPBAudioInput_StartOrStop( + API_ID_PPB_AUDIO_INPUT_DEV, host_resource(), true)); + return PP_TRUE; +} + +PP_Bool AudioInput::InternalStopCapture() { + GetDispatcher()->Send(new PpapiHostMsg_PPBAudioInput_StartOrStop( + API_ID_PPB_AUDIO_INPUT_DEV, host_resource(), false)); + SetStopCaptureState(); + return PP_TRUE; +} + +void AudioInput::InternalClose() { + GetDispatcher()->Send(new PpapiHostMsg_PPBAudioInput_Close( + API_ID_PPB_AUDIO_INPUT_DEV, host_resource())); } PPB_AudioInput_Proxy::PPB_AudioInput_Proxy(Dispatcher* dispatcher) @@ -127,42 +136,61 @@ PPB_AudioInput_Proxy::~PPB_AudioInput_Proxy() { } // static -PP_Resource PPB_AudioInput_Proxy::CreateProxyResource( - PP_Instance instance_id, - PP_Resource config_id, +PP_Resource PPB_AudioInput_Proxy::CreateProxyResource0_1( + PP_Instance instance, + PP_Resource config, PPB_AudioInput_Callback audio_input_callback, void* user_data) { - PluginDispatcher* dispatcher = PluginDispatcher::GetForInstance(instance_id); + PluginDispatcher* dispatcher = PluginDispatcher::GetForInstance(instance); if (!dispatcher) return 0; - EnterResourceNoLock<PPB_AudioConfig_API> config(config_id, true); - if (config.failed()) + HostResource result; + dispatcher->Send(new PpapiHostMsg_PPBAudioInput_Create( + API_ID_PPB_AUDIO_INPUT_DEV, instance, &result)); + if (result.is_null()) + return 0; + + AudioInput* audio_input = new AudioInput(result); + int32_t open_result = audio_input->Open("", config, audio_input_callback, + user_data, AudioInput::MakeIgnoredCompletionCallback()); + if (open_result != PP_OK && open_result != PP_OK_COMPLETIONPENDING) { + delete audio_input; return 0; + } + return audio_input->GetReference(); +} - if (!audio_input_callback) +// static +PP_Resource PPB_AudioInput_Proxy::CreateProxyResource( + PP_Instance instance) { + PluginDispatcher* dispatcher = PluginDispatcher::GetForInstance(instance); + if (!dispatcher) return 0; HostResource result; dispatcher->Send(new PpapiHostMsg_PPBAudioInput_Create( - API_ID_PPB_AUDIO_INPUT_DEV, instance_id, - config.object()->GetSampleRate(), config.object()->GetSampleFrameCount(), - &result)); + API_ID_PPB_AUDIO_INPUT_DEV, instance, &result)); if (result.is_null()) return 0; - return (new AudioInput(result, config_id, audio_input_callback, - user_data))->GetReference(); + return (new AudioInput(result))->GetReference(); } bool PPB_AudioInput_Proxy::OnMessageReceived(const IPC::Message& msg) { bool handled = true; IPC_BEGIN_MESSAGE_MAP(PPB_AudioInput_Proxy, msg) IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBAudioInput_Create, OnMsgCreate) + IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBAudioInput_EnumerateDevices, + OnMsgEnumerateDevices) + IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBAudioInput_Open, OnMsgOpen) IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBAudioInput_StartOrStop, OnMsgStartOrStop) - IPC_MESSAGE_HANDLER(PpapiMsg_PPBAudioInput_NotifyAudioStreamCreated, - OnMsgNotifyAudioStreamCreated) + IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBAudioInput_Close, OnMsgClose) + + IPC_MESSAGE_HANDLER(PpapiMsg_PPBAudioInput_EnumerateDevicesACK, + OnMsgEnumerateDevicesACK) + IPC_MESSAGE_HANDLER(PpapiMsg_PPBAudioInput_OpenACK, OnMsgOpenACK) IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() // TODO(brettw) handle bad messages! @@ -170,42 +198,42 @@ bool PPB_AudioInput_Proxy::OnMessageReceived(const IPC::Message& msg) { return handled; } -void PPB_AudioInput_Proxy::OnMsgCreate(PP_Instance instance_id, - int32_t sample_rate, - uint32_t sample_frame_count, +void PPB_AudioInput_Proxy::OnMsgCreate(PP_Instance instance, HostResource* result) { - thunk::EnterFunction<thunk::ResourceCreationAPI> resource_creation( - instance_id, true); - if (resource_creation.failed()) - return; + thunk::EnterResourceCreation resource_creation(instance); + if (resource_creation.succeeded()) { + result->SetHostResource( + instance, resource_creation.functions()->CreateAudioInput(instance)); + } +} - // Make the resource and get the API pointer to its trusted interface. - result->SetHostResource( - instance_id, - resource_creation.functions()->CreateAudioInputTrusted(instance_id)); - if (result->is_null()) - return; +void PPB_AudioInput_Proxy::OnMsgEnumerateDevices( + const ppapi::HostResource& audio_input) { + EnterHostFromHostResourceForceCallback<PPB_AudioInput_API> enter( + audio_input, callback_factory_, + &PPB_AudioInput_Proxy::EnumerateDevicesACKInHost, audio_input); - // At this point, we've set the result resource, and this is a sync request. - // Anything below this point must issue the AudioInputChannelConnected - // callback to the browser. Since that's an async message, it will be issued - // back to the plugin after the Create function returns (which is good - // because it would be weird to get a connected message with a failure code - // for a resource you haven't finished creating yet). - // + if (enter.succeeded()) + enter.SetResult(enter.object()->EnumerateDevices(NULL, enter.callback())); +} + +void PPB_AudioInput_Proxy::OnMsgOpen(const ppapi::HostResource& audio_input, + const std::string& device_id, + int32_t sample_rate, + uint32_t sample_frame_count) { // The ...ForceCallback class will help ensure the callback is always called. // All error cases must call SetResult on this class. - EnterHostFromHostResourceForceCallback<PPB_AudioInput_API> enter( - *result, callback_factory_, - &PPB_AudioInput_Proxy::AudioInputChannelConnected, *result); + audio_input, callback_factory_, &PPB_AudioInput_Proxy::OpenACKInHost, + audio_input); if (enter.failed()) return; // When enter fails, it will internally schedule the callback. + thunk::EnterResourceCreation resource_creation(audio_input.instance()); // Make an audio config object. PP_Resource audio_config_res = resource_creation.functions()->CreateAudioConfig( - instance_id, static_cast<PP_AudioSampleRate>(sample_rate), + audio_input.instance(), static_cast<PP_AudioSampleRate>(sample_rate), sample_frame_count); if (!audio_config_res) { enter.SetResult(PP_ERROR_FAILED); @@ -213,8 +241,8 @@ void PPB_AudioInput_Proxy::OnMsgCreate(PP_Instance instance_id, } // Initiate opening the audio object. - enter.SetResult(enter.object()->OpenTrusted(audio_config_res, - enter.callback())); + enter.SetResult(enter.object()->OpenTrusted( + device_id, audio_config_res, enter.callback())); // Clean up the temporary audio config resource we made. const PPB_Core* core = static_cast<const PPB_Core*>( @@ -222,10 +250,9 @@ void PPB_AudioInput_Proxy::OnMsgCreate(PP_Instance instance_id, core->ReleaseResource(audio_config_res); } -void PPB_AudioInput_Proxy::OnMsgStartOrStop( - const HostResource& resource, - bool capture) { - EnterHostFromHostResource<PPB_AudioInput_API> enter(resource); +void PPB_AudioInput_Proxy::OnMsgStartOrStop(const HostResource& audio_input, + bool capture) { + EnterHostFromHostResource<PPB_AudioInput_API> enter(audio_input); if (enter.failed()) return; if (capture) @@ -234,15 +261,32 @@ void PPB_AudioInput_Proxy::OnMsgStartOrStop( enter.object()->StopCapture(); } +void PPB_AudioInput_Proxy::OnMsgClose(const ppapi::HostResource& audio_input) { + EnterHostFromHostResource<PPB_AudioInput_API> enter(audio_input); + if (enter.succeeded()) + enter.object()->Close(); +} + // Processed in the plugin (message from host). -void PPB_AudioInput_Proxy::OnMsgNotifyAudioStreamCreated( - const HostResource& audio_id, - int32_t result_code, +void PPB_AudioInput_Proxy::OnMsgEnumerateDevicesACK( + const ppapi::HostResource& audio_input, + int32_t result, + const std::vector<ppapi::DeviceRefData>& devices) { + EnterPluginFromHostResource<PPB_AudioInput_API> enter(audio_input); + if (enter.succeeded()) { + static_cast<AudioInput*>(enter.object())->OnEnumerateDevicesComplete( + result, devices); + } +} + +void PPB_AudioInput_Proxy::OnMsgOpenACK( + const HostResource& audio_input, + int32_t result, IPC::PlatformFileForTransit socket_handle, base::SharedMemoryHandle handle, uint32_t length) { - EnterPluginFromHostResource<PPB_AudioInput_API> enter(audio_id); - if (enter.failed() || result_code != PP_OK) { + EnterPluginFromHostResource<PPB_AudioInput_API> enter(audio_input); + if (enter.failed()) { // The caller may still have given us these handles in the failure case. // The easiest way to clean these up is to just put them in the objects // and then close them. This failure case is not performance critical. @@ -250,23 +294,31 @@ void PPB_AudioInput_Proxy::OnMsgNotifyAudioStreamCreated( IPC::PlatformFileForTransitToPlatformFile(socket_handle)); base::SharedMemory temp_mem(handle, false); } else { - static_cast<AudioInput*>(enter.object())->SetStreamInfo( - handle, length, + static_cast<AudioInput*>(enter.object())->OnOpenComplete( + result, handle, length, IPC::PlatformFileForTransitToPlatformFile(socket_handle)); } } -void PPB_AudioInput_Proxy::AudioInputChannelConnected( +void PPB_AudioInput_Proxy::EnumerateDevicesACKInHost( int32_t result, - const HostResource& resource) { + const HostResource& audio_input) { + EnterHostFromHostResource<PPB_AudioInput_API> enter(audio_input); + dispatcher()->Send(new PpapiMsg_PPBAudioInput_EnumerateDevicesACK( + API_ID_PPB_AUDIO_INPUT_DEV, audio_input, result, + enter.succeeded() && result == PP_OK ? + enter.object()->GetDeviceRefData() : std::vector<DeviceRefData>())); +} + +void PPB_AudioInput_Proxy::OpenACKInHost(int32_t result, + const HostResource& audio_input) { IPC::PlatformFileForTransit socket_handle = IPC::InvalidPlatformFileForTransit(); base::SharedMemoryHandle shared_memory = IPC::InvalidPlatformFileForTransit(); uint32_t shared_memory_length = 0; - int32_t result_code = result; - if (result_code == PP_OK) { - result_code = GetAudioInputConnectedHandles(resource, &socket_handle, + if (result == PP_OK) { + result = GetAudioInputConnectedHandles(audio_input, &socket_handle, &shared_memory, &shared_memory_length); } @@ -275,9 +327,9 @@ void PPB_AudioInput_Proxy::AudioInputChannelConnected( // code since the handles will be in the other process and could be // inconvenient to clean up. Our IPC code will automatically handle this for // us, as long as the remote side always closes the handles it receives - // (in OnMsgNotifyAudioStreamCreated), even in the failure case. - dispatcher()->Send(new PpapiMsg_PPBAudioInput_NotifyAudioStreamCreated( - API_ID_PPB_AUDIO_INPUT_DEV, resource, result_code, socket_handle, + // (in OnMsgOpenACK), even in the failure case. + dispatcher()->Send(new PpapiMsg_PPBAudioInput_OpenACK( + API_ID_PPB_AUDIO_INPUT_DEV, audio_input, result, socket_handle, shared_memory, shared_memory_length)); } diff --git a/ppapi/proxy/ppb_audio_input_proxy.h b/ppapi/proxy/ppb_audio_input_proxy.h index e50b03b..61125c6 100644 --- a/ppapi/proxy/ppb_audio_input_proxy.h +++ b/ppapi/proxy/ppb_audio_input_proxy.h @@ -5,7 +5,9 @@ #ifndef PPAPI_PROXY_PPB_AUDIO_INPUT_PROXY_H_ #define PPAPI_PROXY_PPB_AUDIO_INPUT_PROXY_H_ +#include <string> #include <utility> +#include <vector> #include "base/basictypes.h" #include "base/shared_memory.h" @@ -20,6 +22,7 @@ namespace ppapi { class HostResource; +struct DeviceRefData; namespace proxy { @@ -28,12 +31,14 @@ class PPB_AudioInput_Proxy : public InterfaceProxy { explicit PPB_AudioInput_Proxy(Dispatcher* dispatcher); virtual ~PPB_AudioInput_Proxy(); - static PP_Resource CreateProxyResource( + static PP_Resource CreateProxyResource0_1( PP_Instance instance, - PP_Resource config_id, + PP_Resource config, PPB_AudioInput_Callback audio_input_callback, void* user_data); + static PP_Resource CreateProxyResource(PP_Instance instance); + // InterfaceProxy implementation. virtual bool OnMessageReceived(const IPC::Message& msg); @@ -42,23 +47,31 @@ class PPB_AudioInput_Proxy : public InterfaceProxy { private: // Message handlers. // Plugin->renderer message handlers. - void OnMsgCreate(PP_Instance instance_id, - int32_t sample_rate, - uint32_t sample_frame_count, - ppapi::HostResource* result); - void OnMsgStartOrStop(const ppapi::HostResource& audio_id, bool capture); + void OnMsgCreate(PP_Instance instance, ppapi::HostResource* result); + void OnMsgEnumerateDevices(const ppapi::HostResource& audio_input); + void OnMsgOpen(const ppapi::HostResource& audio_input, + const std::string& device_id, + int32_t sample_rate, + uint32_t sample_frame_count); + void OnMsgStartOrStop(const ppapi::HostResource& audio_input, bool capture); + void OnMsgClose(const ppapi::HostResource& audio_input); // Renderer->plugin message handlers. - void OnMsgNotifyAudioStreamCreated(const ppapi::HostResource& audio_id, - int32_t result_code, - IPC::PlatformFileForTransit socket_handle, - base::SharedMemoryHandle handle, - uint32_t length); - - void AudioInputChannelConnected(int32_t result, - const ppapi::HostResource& resource); - - // In the renderer, this is called in response to a stream created message. + void OnMsgEnumerateDevicesACK( + const ppapi::HostResource& audio_input, + int32_t result, + const std::vector<ppapi::DeviceRefData>& devices); + void OnMsgOpenACK(const ppapi::HostResource& audio_input, + int32_t result, + IPC::PlatformFileForTransit socket_handle, + base::SharedMemoryHandle handle, + uint32_t length); + + void EnumerateDevicesACKInHost(int32_t result, + const ppapi::HostResource& audio_input); + void OpenACKInHost(int32_t result, const ppapi::HostResource& audio_input); + + // In the renderer, this is called in response to an OpenACK message. // It will retrieve the shared memory and socket handles and place them into // the given out params. The return value is a PPAPI error code. // diff --git a/ppapi/proxy/resource_creation_proxy.cc b/ppapi/proxy/resource_creation_proxy.cc index 962c9ad..75c41bd 100644 --- a/ppapi/proxy/resource_creation_proxy.cc +++ b/ppapi/proxy/resource_creation_proxy.cc @@ -84,20 +84,19 @@ PP_Resource ResourceCreationProxy::CreateAudioTrusted(PP_Instance instance) { return 0; } -PP_Resource ResourceCreationProxy::CreateAudioInput( +PP_Resource ResourceCreationProxy::CreateAudioInput0_1( PP_Instance instance, PP_Resource config_id, PPB_AudioInput_Callback audio_input_callback, void* user_data) { - return PPB_AudioInput_Proxy::CreateProxyResource(instance, config_id, - audio_input_callback, - user_data); + return PPB_AudioInput_Proxy::CreateProxyResource0_1(instance, config_id, + audio_input_callback, + user_data); } -PP_Resource ResourceCreationProxy::CreateAudioInputTrusted( +PP_Resource ResourceCreationProxy::CreateAudioInput( PP_Instance instance) { - // Proxied plugins can't created trusted audio input devices. - return 0; + return PPB_AudioInput_Proxy::CreateProxyResource(instance); } PP_Resource ResourceCreationProxy::CreateBroker(PP_Instance instance) { diff --git a/ppapi/proxy/resource_creation_proxy.h b/ppapi/proxy/resource_creation_proxy.h index 2b9428a..8e50896 100644 --- a/ppapi/proxy/resource_creation_proxy.h +++ b/ppapi/proxy/resource_creation_proxy.h @@ -46,12 +46,12 @@ class ResourceCreationProxy : public InterfaceProxy, PP_AudioSampleRate sample_rate, uint32_t sample_frame_count) OVERRIDE; virtual PP_Resource CreateAudioTrusted(PP_Instance instance) OVERRIDE; - virtual PP_Resource CreateAudioInput( + virtual PP_Resource CreateAudioInput0_1( PP_Instance instance, PP_Resource config_id, PPB_AudioInput_Callback audio_input_callback, void* user_data) OVERRIDE; - virtual PP_Resource CreateAudioInputTrusted(PP_Instance instance) OVERRIDE; + virtual PP_Resource CreateAudioInput(PP_Instance instance) OVERRIDE; virtual PP_Resource CreateBroker(PP_Instance instance) OVERRIDE; virtual PP_Resource CreateBrowserFont( PP_Instance instance, diff --git a/ppapi/shared_impl/ppb_audio_input_shared.cc b/ppapi/shared_impl/ppb_audio_input_shared.cc index ae5b37e..3028894 100644 --- a/ppapi/shared_impl/ppb_audio_input_shared.cc +++ b/ppapi/shared_impl/ppb_audio_input_shared.cc @@ -1,21 +1,117 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// 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 "ppapi/shared_impl/ppb_audio_input_shared.h" #include "base/logging.h" +#include "ppapi/c/pp_errors.h" +#include "ppapi/shared_impl/ppapi_globals.h" +#include "ppapi/shared_impl/ppb_device_ref_shared.h" +#include "ppapi/shared_impl/resource_tracker.h" +#include "ppapi/thunk/enter.h" +#include "ppapi/thunk/ppb_audio_config_api.h" namespace ppapi { -PPB_AudioInput_Shared::PPB_AudioInput_Shared() - : capturing_(false), +namespace { + +void IgnoredCompletionCallback(void* user_data, int32_t result) { + // Do nothing. +} + +} // namespace + +PPB_AudioInput_Shared::PPB_AudioInput_Shared(const HostResource& audio_input) + : Resource(OBJECT_IS_PROXY, audio_input), + open_state_(BEFORE_OPEN), + capturing_(false), + shared_memory_size_(0), + audio_input_callback_(NULL), + user_data_(NULL), + devices_(NULL), + resource_object_type_(OBJECT_IS_PROXY) { +} + +PPB_AudioInput_Shared::PPB_AudioInput_Shared(PP_Instance instance) + : Resource(OBJECT_IS_IMPL, instance), + open_state_(BEFORE_OPEN), + capturing_(false), shared_memory_size_(0), - callback_(NULL), - user_data_(NULL) { + audio_input_callback_(NULL), + user_data_(NULL), + devices_(NULL), + resource_object_type_(OBJECT_IS_IMPL) { } PPB_AudioInput_Shared::~PPB_AudioInput_Shared() { + DCHECK(open_state_ == CLOSED); +} + +thunk::PPB_AudioInput_API* PPB_AudioInput_Shared::AsPPB_AudioInput_API() { + return this; +} + +int32_t PPB_AudioInput_Shared::EnumerateDevices( + PP_Resource* devices, + PP_CompletionCallback callback) { + if (!callback.func) + return PP_ERROR_BLOCKS_MAIN_THREAD; + if (TrackedCallback::IsPending(enumerate_devices_callback_)) + return PP_ERROR_INPROGRESS; + + return InternalEnumerateDevices(devices, callback); +} + +int32_t PPB_AudioInput_Shared::Open( + const std::string& device_id, + PP_Resource config, + PPB_AudioInput_Callback audio_input_callback, + void* user_data, + PP_CompletionCallback callback) { + if (!audio_input_callback) + return PP_ERROR_BADARGUMENT; + + return CommonOpen(device_id, config, audio_input_callback, user_data, + callback); +} + +PP_Resource PPB_AudioInput_Shared::GetCurrentConfig() { + // AddRef for the caller. + if (config_.get()) + PpapiGlobals::Get()->GetResourceTracker()->AddRefResource(config_); + return config_; +} + +PP_Bool PPB_AudioInput_Shared::StartCapture() { + if (open_state_ == CLOSED || (open_state_ == BEFORE_OPEN && + !TrackedCallback::IsPending(open_callback_))) { + return PP_FALSE; + } + if (capturing_) + return PP_TRUE; + + return InternalStartCapture(); +} + +PP_Bool PPB_AudioInput_Shared::StopCapture() { + if (open_state_ == CLOSED || (open_state_ == BEFORE_OPEN && + !TrackedCallback::IsPending(open_callback_))) { + return PP_FALSE; + } + if (!capturing_) + return PP_TRUE; + + return InternalStopCapture(); +} + +void PPB_AudioInput_Shared::Close() { + if (open_state_ == CLOSED) + return; + + open_state_ = CLOSED; + InternalClose(); + // Closing the socket causes the thread to exit - wait for it. if (socket_.get()) socket_->Close(); @@ -23,12 +119,47 @@ PPB_AudioInput_Shared::~PPB_AudioInput_Shared() { audio_input_thread_->Join(); audio_input_thread_.reset(); } + + if (TrackedCallback::IsPending(open_callback_)) + open_callback_->PostAbort(); } -void PPB_AudioInput_Shared::SetCallback(PPB_AudioInput_Callback callback, - void* user_data) { - callback_ = callback; - user_data_ = user_data; +void PPB_AudioInput_Shared::OnEnumerateDevicesComplete( + int32_t result, + const std::vector<DeviceRefData>& devices) { + DCHECK(TrackedCallback::IsPending(enumerate_devices_callback_)); + + if (result == PP_OK && devices_) { + *devices_ = PPB_DeviceRef_Shared::CreateResourceArray( + resource_object_type_, pp_instance(), devices); + } + devices_ = NULL; + + TrackedCallback::ClearAndRun(&enumerate_devices_callback_, result); +} + +void PPB_AudioInput_Shared::OnOpenComplete( + int32_t result, + base::SharedMemoryHandle shared_memory_handle, + size_t shared_memory_size, + base::SyncSocket::Handle socket_handle) { + if (open_state_ == BEFORE_OPEN && result == PP_OK) { + open_state_ = OPENED; + SetStreamInfo(shared_memory_handle, shared_memory_size, socket_handle); + } else { + // Clean up the handles. + base::SyncSocket temp_socket(socket_handle); + base::SharedMemory temp_mem(shared_memory_handle, false); + } + + // The callback may have been aborted by Close(). + if (TrackedCallback::IsPending(open_callback_)) + TrackedCallback::ClearAndRun(&open_callback_, result); +} + +// static +PP_CompletionCallback PPB_AudioInput_Shared::MakeIgnoredCompletionCallback() { + return PP_MakeCompletionCallback(&IgnoredCompletionCallback, NULL); } void PPB_AudioInput_Shared::SetStartCaptureState() { @@ -37,10 +168,10 @@ void PPB_AudioInput_Shared::SetStartCaptureState() { // If the socket doesn't exist, that means that the plugin has started before // the browser has had a chance to create all the shared memory info and - // notify us. This is a common case. In this case, we just set the playing_ + // notify us. This is a common case. In this case, we just set the capturing_ // flag and the capture will automatically start when that data is available // in SetStreamInfo. - if (socket_.get()) + if (audio_input_callback_ && socket_.get()) StartThread(); capturing_ = true; } @@ -63,17 +194,17 @@ void PPB_AudioInput_Shared::SetStreamInfo( shared_memory_.reset(new base::SharedMemory(shared_memory_handle, false)); shared_memory_size_ = shared_memory_size; - if (callback_) { + if (audio_input_callback_) { shared_memory_->Map(shared_memory_size_); - // In common case StartCapture() was called before StreamCreated(). + // StartCapture() may be called before SetSreamInfo(). if (capturing_) StartThread(); } } void PPB_AudioInput_Shared::StartThread() { - DCHECK(callback_); + DCHECK(audio_input_callback_); DCHECK(!audio_input_thread_.get()); audio_input_thread_.reset(new base::DelegateSimpleThread( this, "plugin_audio_input_thread")); @@ -87,8 +218,36 @@ void PPB_AudioInput_Shared::Run() { while (sizeof(pending_data) == socket_->Receive(&pending_data, sizeof(pending_data)) && pending_data >= 0) { - callback_(buffer, shared_memory_size_, user_data_); + audio_input_callback_(buffer, shared_memory_size_, user_data_); } } +int32_t PPB_AudioInput_Shared::CommonOpen( + const std::string& device_id, + PP_Resource config, + PPB_AudioInput_Callback audio_input_callback, + void* user_data, + PP_CompletionCallback callback) { + if (open_state_ != BEFORE_OPEN) + return PP_ERROR_FAILED; + + thunk::EnterResourceNoLock<thunk::PPB_AudioConfig_API> enter_config(config, + true); + if (enter_config.failed()) + return PP_ERROR_BADARGUMENT; + + if (!callback.func) + return PP_ERROR_BLOCKS_MAIN_THREAD; + + if (TrackedCallback::IsPending(open_callback_)) + return PP_ERROR_INPROGRESS; + + config_ = config; + audio_input_callback_ = audio_input_callback; + user_data_ = user_data; + + return InternalOpen(device_id, enter_config.object()->GetSampleRate(), + enter_config.object()->GetSampleFrameCount(), callback); +} + } // namespace ppapi diff --git a/ppapi/shared_impl/ppb_audio_input_shared.h b/ppapi/shared_impl/ppb_audio_input_shared.h index 5c5e88d..5d44ddc 100644 --- a/ppapi/shared_impl/ppb_audio_input_shared.h +++ b/ppapi/shared_impl/ppb_audio_input_shared.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// 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. @@ -6,12 +6,17 @@ #define PPAPI_SHARED_IMPL_PPB_AUDIO_INPUT_SHARED_H_ #include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/shared_memory.h" #include "base/sync_socket.h" #include "base/threading/simple_thread.h" #include "ppapi/c/dev/ppb_audio_input_dev.h" +#include "ppapi/c/ppb_audio_config.h" #include "ppapi/shared_impl/resource.h" +#include "ppapi/shared_impl/scoped_pp_resource.h" +#include "ppapi/shared_impl/tracked_callback.h" #include "ppapi/thunk/ppb_audio_input_api.h" namespace ppapi { @@ -20,38 +25,77 @@ namespace ppapi { // from the sync socket. Both the proxy and the renderer implementation use // this code. class PPAPI_SHARED_EXPORT PPB_AudioInput_Shared - : public thunk::PPB_AudioInput_API, + : public Resource, + public thunk::PPB_AudioInput_API, public base::DelegateSimpleThread::Delegate { public: - PPB_AudioInput_Shared(); + // Used by the proxy. + explicit PPB_AudioInput_Shared(const HostResource& audio_input); + // Used by the impl. + explicit PPB_AudioInput_Shared(PP_Instance instance); virtual ~PPB_AudioInput_Shared(); - bool capturing() const { return capturing_; } - - // Sets the callback information that the background thread will use. This - // is optional. Without a callback, the thread will not be run. This - // non-callback mode is used in the renderer with the proxy, since the proxy - // handles the callback entirely within the plugin process. - void SetCallback(PPB_AudioInput_Callback callback, void* user_data); - - // Configures the current state to be playing or not. The caller is + // Resource overrides. + virtual thunk::PPB_AudioInput_API* AsPPB_AudioInput_API() OVERRIDE; + + // Implementation of PPB_AudioInput_API non-trusted methods. + virtual int32_t EnumerateDevices(PP_Resource* devices, + PP_CompletionCallback callback) OVERRIDE; + virtual int32_t Open(const std::string& device_id, + PP_Resource config, + PPB_AudioInput_Callback audio_input_callback, + void* user_data, + PP_CompletionCallback callback) OVERRIDE; + virtual PP_Resource GetCurrentConfig() OVERRIDE; + virtual PP_Bool StartCapture() OVERRIDE; + virtual PP_Bool StopCapture() OVERRIDE; + virtual void Close() OVERRIDE; + + void OnEnumerateDevicesComplete(int32_t result, + const std::vector<DeviceRefData>& devices); + void OnOpenComplete(int32_t result, + base::SharedMemoryHandle shared_memory_handle, + size_t shared_memory_size, + base::SyncSocket::Handle socket_handle); + + static PP_CompletionCallback MakeIgnoredCompletionCallback(); + + protected: + enum OpenState { + BEFORE_OPEN, + OPENED, + CLOSED + }; + + // Subclasses should implement these methods to do impl- and proxy-specific + // work. + virtual int32_t InternalEnumerateDevices(PP_Resource* devices, + PP_CompletionCallback callback) = 0; + virtual int32_t InternalOpen(const std::string& device_id, + PP_AudioSampleRate sample_rate, + uint32_t sample_frame_count, + PP_CompletionCallback callback) = 0; + virtual PP_Bool InternalStartCapture() = 0; + virtual PP_Bool InternalStopCapture() = 0; + virtual void InternalClose() = 0; + + // Configures the current state to be capturing or not. The caller is // responsible for ensuring the new state is the opposite of the current one. // // This is the implementation for PPB_AudioInput.Start/StopCapture, except // that it does not actually notify the audio system to stop capture, it just // configures our object to stop generating callbacks. The actual stop - // playback request will be done in the derived classes and will be different + // capture request will be done in the derived classes and will be different // from the proxy and the renderer. void SetStartCaptureState(); void SetStopCaptureState(); // Sets the shared memory and socket handles. This will automatically start - // playback if we're currently set to play. + // capture if we're currently set to capture. void SetStreamInfo(base::SharedMemoryHandle shared_memory_handle, size_t shared_memory_size, base::SyncSocket::Handle socket_handle); - private: // Starts execution of the audio input thread. void StartThread(); @@ -59,15 +103,29 @@ class PPAPI_SHARED_EXPORT PPB_AudioInput_Shared // Run on the audio input thread. virtual void Run(); + // The common implementation of OpenTrusted() and Open(). It will call + // InternalOpen() to do impl- and proxy-specific work. + // OpenTrusted() will call this methods with a NULL |audio_input_callback|, + // in this case, the thread will not be run. This non-callback mode is used in + // the renderer with the proxy, since the proxy handles the callback entirely + // within the plugin process. + int32_t CommonOpen(const std::string& device_id, + PP_Resource config, + PPB_AudioInput_Callback audio_input_callback, + void* user_data, + PP_CompletionCallback callback); + + OpenState open_state_; + // True if capturing the stream. bool capturing_; - // Socket used to notify us when audio is ready to accept new samples. This - // pointer is created in StreamCreated(). + // Socket used to notify us when new samples are available. This pointer is + // created in SetStreamInfo(). scoped_ptr<base::SyncSocket> socket_; // Sample buffer in shared memory. This pointer is created in - // StreamCreated(). The memory is only mapped when the audio thread is + // SetStreamInfo(). The memory is only mapped when the audio thread is // created. scoped_ptr<base::SharedMemory> shared_memory_; @@ -77,12 +135,25 @@ class PPAPI_SHARED_EXPORT PPB_AudioInput_Shared // When the callback is set, this thread is spawned for calling it. scoped_ptr<base::DelegateSimpleThread> audio_input_thread_; - // Callback to call when audio is ready to produce new samples. - PPB_AudioInput_Callback callback_; + // Callback to call when new samples are available. + PPB_AudioInput_Callback audio_input_callback_; // User data pointer passed verbatim to the callback function. void* user_data_; + scoped_refptr<TrackedCallback> enumerate_devices_callback_; + scoped_refptr<TrackedCallback> open_callback_; + + // Owning reference to the current config object. This isn't actually used, + // we just dish it out as requested by the plugin. + ScopedPPResource config_; + + // Output parameter of EnumerateDevices(). It should not be accessed after + // |enumerate_devices_callback_| is run. + PP_Resource* devices_; + + ResourceObjectType resource_object_type_; + DISALLOW_COPY_AND_ASSIGN(PPB_AudioInput_Shared); }; diff --git a/ppapi/thunk/interfaces_ppb_public_dev.h b/ppapi/thunk/interfaces_ppb_public_dev.h index 5a7ca47..037dce8 100644 --- a/ppapi/thunk/interfaces_ppb_public_dev.h +++ b/ppapi/thunk/interfaces_ppb_public_dev.h @@ -24,6 +24,8 @@ UNPROXIED_API(PPB_Widget) PROXIED_IFACE(PPB_AudioInput, PPB_AUDIO_INPUT_DEV_INTERFACE_0_1, PPB_AudioInput_Dev_0_1) +PROXIED_IFACE(PPB_AudioInput, PPB_AUDIO_INPUT_DEV_INTERFACE_0_2, + PPB_AudioInput_Dev_0_2) PROXIED_IFACE(NoAPIName, PPB_IME_INPUT_EVENT_DEV_INTERFACE_0_1, PPB_IMEInputEvent_Dev_0_1) PROXIED_IFACE(PPB_Buffer, PPB_BUFFER_DEV_INTERFACE_0_4, PPB_Buffer_Dev_0_4) diff --git a/ppapi/thunk/ppb_audio_input_api.h b/ppapi/thunk/ppb_audio_input_api.h index 41cb4ef..68c8b94 100644 --- a/ppapi/thunk/ppb_audio_input_api.h +++ b/ppapi/thunk/ppb_audio_input_api.h @@ -1,30 +1,46 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// 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. #ifndef PPAPI_THUNK_AUDIO_INPUT_API_H_ #define PPAPI_THUNK_AUDIO_INPUT_API_H_ +#include <string> +#include <vector> + #include "ppapi/c/dev/ppb_audio_input_dev.h" #include "ppapi/c/pp_completion_callback.h" #include "ppapi/thunk/ppapi_thunk_export.h" namespace ppapi { + +struct DeviceRefData; + namespace thunk { class PPAPI_THUNK_EXPORT PPB_AudioInput_API { public: virtual ~PPB_AudioInput_API() {} + virtual int32_t EnumerateDevices(PP_Resource* devices, + PP_CompletionCallback callback) = 0; + virtual int32_t Open(const std::string& device_id, + PP_Resource config, + PPB_AudioInput_Callback audio_input_callback, + void* user_data, + PP_CompletionCallback callback) = 0; virtual PP_Resource GetCurrentConfig() = 0; virtual PP_Bool StartCapture() = 0; virtual PP_Bool StopCapture() = 0; + virtual void Close() = 0; // Trusted API. - virtual int32_t OpenTrusted(PP_Resource config_id, + virtual int32_t OpenTrusted(const std::string& device_id, + PP_Resource config, PP_CompletionCallback create_callback) = 0; virtual int32_t GetSyncSocket(int* sync_socket) = 0; virtual int32_t GetSharedMemory(int* shm_handle, uint32_t* shm_size) = 0; + virtual const std::vector<DeviceRefData>& GetDeviceRefData() const = 0; }; } // namespace thunk diff --git a/ppapi/thunk/ppb_audio_input_thunk.cc b/ppapi/thunk/ppb_audio_input_thunk.cc index f2a65c8..9102dc5 100644 --- a/ppapi/thunk/ppb_audio_input_thunk.cc +++ b/ppapi/thunk/ppb_audio_input_thunk.cc @@ -3,7 +3,9 @@ // found in the LICENSE file. #include "ppapi/c/pp_errors.h" +#include "ppapi/shared_impl/ppb_device_ref_shared.h" #include "ppapi/thunk/enter.h" +#include "ppapi/thunk/ppb_device_ref_api.h" #include "ppapi/thunk/ppb_audio_input_api.h" #include "ppapi/thunk/resource_creation_api.h" #include "ppapi/thunk/thunk.h" @@ -15,16 +17,24 @@ namespace { typedef EnterResource<PPB_AudioInput_API> EnterAudioInput; -PP_Resource Create(PP_Instance instance, - PP_Resource config_id, - PPB_AudioInput_Callback callback, - void* user_data) { - EnterFunction<ResourceCreationAPI> enter(instance, true); +PP_Resource Create0_1(PP_Instance instance, + PP_Resource config, + PPB_AudioInput_Callback audio_input_callback, + void* user_data) { + EnterResourceCreation enter(instance); if (enter.failed()) return 0; - return enter.functions()->CreateAudioInput(instance, config_id, - callback, user_data); + return enter.functions()->CreateAudioInput0_1( + instance, config, audio_input_callback, user_data); +} + +PP_Resource Create0_2(PP_Instance instance) { + EnterResourceCreation enter(instance); + if (enter.failed()) + return 0; + + return enter.functions()->CreateAudioInput(instance); } PP_Bool IsAudioInput(PP_Resource resource) { @@ -32,8 +42,42 @@ PP_Bool IsAudioInput(PP_Resource resource) { return PP_FromBool(enter.succeeded()); } -PP_Resource GetCurrentConfiguration(PP_Resource audio_id) { - EnterAudioInput enter(audio_id, true); +int32_t EnumerateDevices(PP_Resource audio_input, + PP_Resource* devices, + PP_CompletionCallback callback) { + EnterAudioInput enter(audio_input, callback, true); + if (enter.failed()) + return enter.retval(); + + return enter.SetResult(enter.object()->EnumerateDevices(devices, callback)); +} + +int32_t Open(PP_Resource audio_input, + PP_Resource device_ref, + PP_Resource config, + PPB_AudioInput_Callback audio_input_callback, + void* user_data, + PP_CompletionCallback callback) { + EnterAudioInput enter(audio_input, callback, true); + if (enter.failed()) + return enter.retval(); + + std::string device_id; + // |device_id| remains empty if |device_ref| is 0, which means the default + // device. + if (device_ref != 0) { + EnterResourceNoLock<PPB_DeviceRef_API> enter_device_ref(device_ref, true); + if (enter_device_ref.failed()) + return enter.SetResult(PP_ERROR_BADRESOURCE); + device_id = enter_device_ref.object()->GetDeviceRefData().id; + } + + return enter.SetResult(enter.object()->Open( + device_id, config, audio_input_callback, user_data, callback)); +} + +PP_Resource GetCurrentConfig(PP_Resource audio_input) { + EnterAudioInput enter(audio_input, true); if (enter.failed()) return 0; return enter.object()->GetCurrentConfig(); @@ -55,18 +99,39 @@ PP_Bool StopCapture(PP_Resource audio_input) { return enter.object()->StopCapture(); } -const PPB_AudioInput_Dev g_ppb_audioinput_thunk = { - &Create, +void Close(PP_Resource audio_input) { + EnterAudioInput enter(audio_input, true); + if (enter.succeeded()) + enter.object()->Close(); +} + +const PPB_AudioInput_Dev_0_1 g_ppb_audioinput_0_1_thunk = { + &Create0_1, &IsAudioInput, - &GetCurrentConfiguration, + &GetCurrentConfig, &StartCapture, &StopCapture }; +const PPB_AudioInput_Dev_0_2 g_ppb_audioinput_0_2_thunk = { + &Create0_2, + &IsAudioInput, + &EnumerateDevices, + &Open, + &GetCurrentConfig, + &StartCapture, + &StopCapture, + &Close +}; + } // namespace const PPB_AudioInput_Dev_0_1* GetPPB_AudioInput_Dev_0_1_Thunk() { - return &g_ppb_audioinput_thunk; + return &g_ppb_audioinput_0_1_thunk; +} + +const PPB_AudioInput_Dev_0_2* GetPPB_AudioInput_Dev_0_2_Thunk() { + return &g_ppb_audioinput_0_2_thunk; } } // namespace thunk diff --git a/ppapi/thunk/ppb_audio_input_trusted_thunk.cc b/ppapi/thunk/ppb_audio_input_trusted_thunk.cc index ac9f0bb..b17e1bd 100644 --- a/ppapi/thunk/ppb_audio_input_trusted_thunk.cc +++ b/ppapi/thunk/ppb_audio_input_trusted_thunk.cc @@ -20,7 +20,7 @@ PP_Resource Create(PP_Instance instance_id) { EnterFunction<ResourceCreationAPI> enter(instance_id, true); if (enter.failed()) return 0; - return enter.functions()->CreateAudioInputTrusted(instance_id); + return enter.functions()->CreateAudioInput(instance_id); } int32_t Open(PP_Resource audio_id, @@ -29,7 +29,7 @@ int32_t Open(PP_Resource audio_id, EnterAudioInput enter(audio_id, callback, true); if (enter.failed()) return enter.retval(); - return enter.SetResult(enter.object()->OpenTrusted(config_id, callback)); + return enter.SetResult(enter.object()->OpenTrusted("", config_id, callback)); } int32_t GetSyncSocket(PP_Resource audio_id, int* sync_socket) { diff --git a/ppapi/thunk/resource_creation_api.h b/ppapi/thunk/resource_creation_api.h index 8b9360f5..d78f226 100644 --- a/ppapi/thunk/resource_creation_api.h +++ b/ppapi/thunk/resource_creation_api.h @@ -50,12 +50,12 @@ class ResourceCreationAPI { virtual PP_Resource CreateAudioConfig(PP_Instance instance, PP_AudioSampleRate sample_rate, uint32_t sample_frame_count) = 0; - virtual PP_Resource CreateAudioInput( + virtual PP_Resource CreateAudioInput0_1( PP_Instance instance, PP_Resource config_id, PPB_AudioInput_Callback audio_input_callback, void* user_data) = 0; - virtual PP_Resource CreateAudioInputTrusted(PP_Instance instance) = 0; + virtual PP_Resource CreateAudioInput(PP_Instance instance) = 0; virtual PP_Resource CreateBroker(PP_Instance instance) = 0; virtual PP_Resource CreateBrowserFont( PP_Instance instance, |