diff options
author | yzshen@chromium.org <yzshen@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-12-05 02:44:18 +0000 |
---|---|---|
committer | yzshen@chromium.org <yzshen@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-12-05 02:44:18 +0000 |
commit | 4f01c76ed760e674cb86f2b0a17a779431e2aa71 (patch) | |
tree | a2d874fb3f21b65e07f0d75db567487f6bd4a38a /ppapi | |
parent | 4afef6e27ecbcf6430c5d37881eca04c6b1f8d78 (diff) | |
download | chromium_src-4f01c76ed760e674cb86f2b0a17a779431e2aa71.zip chromium_src-4f01c76ed760e674cb86f2b0a17a779431e2aa71.tar.gz chromium_src-4f01c76ed760e674cb86f2b0a17a779431e2aa71.tar.bz2 |
Introduce PPB_AudioInput_Dev v0.3 and refactor the device enumeration code:
- Add MonitorDeviceChange() for PPB_AudioInput_Dev.
- Change EnumerateDevices() to use PP_ArrayOutput.
- Move device enumeration code out of the audio input implementation, so that it can be shared by video capture.
- Update the audio_input manual test.
- Add unittests for the device enumeration code.
TEST=None
BUG=137799
Review URL: https://chromiumcodereview.appspot.com/11411047
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@171132 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ppapi')
23 files changed, 1210 insertions, 157 deletions
diff --git a/ppapi/api/dev/ppb_audio_input_dev.idl b/ppapi/api/dev/ppb_audio_input_dev.idl index 11f4448..9fe406c 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 { - M19 = 0.2 + M19 = 0.2, + M25 = 0.3 }; /** @@ -79,17 +80,62 @@ interface PPB_AudioInput_Dev { * @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 + * @param[in] callback A <code>PP_CompletionCallback</code> to run on * completion. * * @return An error code from <code>pp_errors.h</code>. */ + [deprecate=0.3] int32_t EnumerateDevices( [in] PP_Resource audio_input, [out] PP_Resource devices, [in] PP_CompletionCallback callback); /** + * Enumerates audio input devices. + * + * @param[in] audio_input A <code>PP_Resource</code> corresponding to an audio + * input resource. + * @param[in] output An output array which will receive + * <code>PPB_DeviceRef_Dev</code> resources on success. Please note that the + * ref count of those resources has already been increased by 1 for the + * caller. + * @param[in] callback A <code>PP_CompletionCallback</code> to run on + * completion. + * + * @return An error code from <code>pp_errors.h</code>. + */ + [version=0.3] + int32_t EnumerateDevices( + [in] PP_Resource audio_input, + [in] PP_ArrayOutput output, + [in] PP_CompletionCallback callback); + + /** + * Requests device change notifications. + * + * @param[in] audio_input A <code>PP_Resource</code> corresponding to an audio + * input resource. + * @param[in] callback The callback to receive notifications. If not NULL, it + * will be called once for the currently available devices, and then every + * time the list of available devices changes. All calls will happen on the + * same thread as the one on which MonitorDeviceChange() is called. It will + * receive notifications until <code>audio_input</code> is destroyed or + * <code>MonitorDeviceChange()</code> is called to set a new callback for + * <code>audio_input</code>. You can pass NULL to cancel sending + * notifications. + * @param[inout] user_data An opaque pointer that will be passed to + * <code>callback</code>. + * + * @return An error code from <code>pp_errors.h</code>. + */ + [version=0.3] + int32_t MonitorDeviceChange( + [in] PP_Resource audio_input, + [in] PP_MonitorDeviceChangeCallback callback, + [inout] mem_t user_data); + + /** * Opens an audio input device. No sound will be captured until * StartCapture() is called. * diff --git a/ppapi/api/dev/ppb_device_ref_dev.idl b/ppapi/api/dev/ppb_device_ref_dev.idl index 660e389..98d1272 100644 --- a/ppapi/api/dev/ppb_device_ref_dev.idl +++ b/ppapi/api/dev/ppb_device_ref_dev.idl @@ -14,6 +14,22 @@ label Chrome { }; /** + * Defines the callback type to receive device change notifications for + * <code>PPB_AudioInput_Dev.MonitorDeviceChange()</code> and + * <code>PPB_VideoCapture_Dev.MonitorDeviceChange()</code>. + * + * @param[inout] user_data The opaque pointer that the caller passed into + * <code>MonitorDeviceChange()</code>. + * @param[in] device_count How many devices in the array. + * @param[in] devices An array of <code>PPB_DeviceRef_Dev</code>. Please note + * that the ref count of the elements is not increased on behalf of the plugin. + */ +typedef void PP_MonitorDeviceChangeCallback( + [inout] mem_t user_data, + [in] uint32_t device_count, + [in, size_is(device_count)] PP_Resource[] devices); + +/** * Device types. */ [assert_size(4)] diff --git a/ppapi/c/dev/ppb_audio_input_dev.h b/ppapi/c/dev/ppb_audio_input_dev.h index 276b5fa7..b47c7ea 100644 --- a/ppapi/c/dev/ppb_audio_input_dev.h +++ b/ppapi/c/dev/ppb_audio_input_dev.h @@ -3,11 +3,13 @@ * found in the LICENSE file. */ -/* From dev/ppb_audio_input_dev.idl modified Wed Nov 14 15:08:54 2012. */ +/* From dev/ppb_audio_input_dev.idl modified Tue Dec 04 15:13:31 2012. */ #ifndef PPAPI_C_DEV_PPB_AUDIO_INPUT_DEV_H_ #define PPAPI_C_DEV_PPB_AUDIO_INPUT_DEV_H_ +#include "ppapi/c/dev/ppb_device_ref_dev.h" +#include "ppapi/c/pp_array_output.h" #include "ppapi/c/pp_bool.h" #include "ppapi/c/pp_completion_callback.h" #include "ppapi/c/pp_instance.h" @@ -16,7 +18,8 @@ #include "ppapi/c/pp_stdint.h" #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 +#define PPB_AUDIO_INPUT_DEV_INTERFACE_0_3 "PPB_AudioInput(Dev);0.3" +#define PPB_AUDIO_INPUT_DEV_INTERFACE PPB_AUDIO_INPUT_DEV_INTERFACE_0_3 /** * @file @@ -62,7 +65,7 @@ typedef void (*PPB_AudioInput_Callback)(const void* sample_buffer, * device. We may want to move the "recommend" functions to the input or output * classes rather than the config. */ -struct PPB_AudioInput_Dev_0_2 { +struct PPB_AudioInput_Dev_0_3 { /** * Creates an audio input resource. * @@ -85,28 +88,42 @@ struct PPB_AudioInput_Dev_0_2 { /** * 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 + * @param[in] output An output array which will receive + * <code>PPB_DeviceRef_Dev</code> resources on success. Please note that the + * ref count of those resources has already been increased by 1 for the + * caller. + * @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_ArrayOutput output, struct PP_CompletionCallback callback); /** + * Requests device change notifications. + * + * @param[in] audio_input A <code>PP_Resource</code> corresponding to an audio + * input resource. + * @param[in] callback The callback to receive notifications. If not NULL, it + * will be called once for the currently available devices, and then every + * time the list of available devices changes. All calls will happen on the + * same thread as the one on which MonitorDeviceChange() is called. It will + * receive notifications until <code>audio_input</code> is destroyed or + * <code>MonitorDeviceChange()</code> is called to set a new callback for + * <code>audio_input</code>. You can pass NULL to cancel sending + * notifications. + * @param[inout] user_data An opaque pointer that will be passed to + * <code>callback</code>. + * + * @return An error code from <code>pp_errors.h</code>. + */ + int32_t (*MonitorDeviceChange)(PP_Resource audio_input, + PP_MonitorDeviceChangeCallback callback, + void* user_data); + /** * Opens an audio input device. No sound will be captured until * StartCapture() is called. * @@ -179,7 +196,25 @@ struct PPB_AudioInput_Dev_0_2 { void (*Close)(PP_Resource audio_input); }; -typedef struct PPB_AudioInput_Dev_0_2 PPB_AudioInput_Dev; +typedef struct PPB_AudioInput_Dev_0_3 PPB_AudioInput_Dev; + +struct PPB_AudioInput_Dev_0_2 { + PP_Resource (*Create)(PP_Instance instance); + PP_Bool (*IsAudioInput)(PP_Resource resource); + int32_t (*EnumerateDevices)(PP_Resource audio_input, + PP_Resource* devices, + struct PP_CompletionCallback callback); + 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); + PP_Resource (*GetCurrentConfig)(PP_Resource audio_input); + PP_Bool (*StartCapture)(PP_Resource audio_input); + PP_Bool (*StopCapture)(PP_Resource audio_input); + void (*Close)(PP_Resource audio_input); +}; /** * @} */ diff --git a/ppapi/c/dev/ppb_device_ref_dev.h b/ppapi/c/dev/ppb_device_ref_dev.h index 7381880..62d75a6 100644 --- a/ppapi/c/dev/ppb_device_ref_dev.h +++ b/ppapi/c/dev/ppb_device_ref_dev.h @@ -3,7 +3,7 @@ * found in the LICENSE file. */ -/* From dev/ppb_device_ref_dev.idl modified Fri Jan 20 12:58:06 2012. */ +/* From dev/ppb_device_ref_dev.idl modified Wed Nov 07 13:28:37 2012. */ #ifndef PPAPI_C_DEV_PPB_DEVICE_REF_DEV_H_ #define PPAPI_C_DEV_PPB_DEVICE_REF_DEV_H_ @@ -24,6 +24,28 @@ /** + * @addtogroup Typedefs + * @{ + */ +/** + * Defines the callback type to receive device change notifications for + * <code>PPB_AudioInput_Dev.MonitorDeviceChange()</code> and + * <code>PPB_VideoCapture_Dev.MonitorDeviceChange()</code>. + * + * @param[inout] user_data The opaque pointer that the caller passed into + * <code>MonitorDeviceChange()</code>. + * @param[in] device_count How many devices in the array. + * @param[in] devices An array of <code>PPB_DeviceRef_Dev</code>. Please note + * that the ref count of the elements is not increased on behalf of the plugin. + */ +typedef void (*PP_MonitorDeviceChangeCallback)(void* user_data, + uint32_t device_count, + const PP_Resource devices[]); +/** + * @} + */ + +/** * @addtogroup Enums * @{ */ diff --git a/ppapi/cpp/dev/audio_input_dev.cc b/ppapi/cpp/dev/audio_input_dev.cc index 4c161a1..cc0f08b 100644 --- a/ppapi/cpp/dev/audio_input_dev.cc +++ b/ppapi/cpp/dev/audio_input_dev.cc @@ -18,13 +18,20 @@ 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_3>() { + return PPB_AUDIO_INPUT_DEV_INTERFACE_0_3; +} + } // namespace AudioInput_Dev::AudioInput_Dev() { } AudioInput_Dev::AudioInput_Dev(const InstanceHandle& instance) { - if (has_interface<PPB_AudioInput_Dev_0_2>()) { + if (has_interface<PPB_AudioInput_Dev_0_3>()) { + PassRefFromConstructor(get_interface<PPB_AudioInput_Dev_0_3>()->Create( + instance.pp_instance())); + } else if (has_interface<PPB_AudioInput_Dev_0_2>()) { PassRefFromConstructor(get_interface<PPB_AudioInput_Dev_0_2>()->Create( instance.pp_instance())); } @@ -35,24 +42,42 @@ AudioInput_Dev::~AudioInput_Dev() { // static bool AudioInput_Dev::IsAvailable() { - return has_interface<PPB_AudioInput_Dev_0_2>(); + return has_interface<PPB_AudioInput_Dev_0_3>() || + has_interface<PPB_AudioInput_Dev_0_2>(); } int32_t AudioInput_Dev::EnumerateDevices( const CompletionCallbackWithOutput<std::vector<DeviceRef_Dev> >& callback) { - if (!has_interface<PPB_AudioInput_Dev_0_2>()) - return callback.MayForce(PP_ERROR_NOINTERFACE); - if (!callback.pp_completion_callback().func) - return callback.MayForce(PP_ERROR_BLOCKS_MAIN_THREAD); - - // ArrayOutputCallbackConverter is responsible to delete it. - ResourceArray_Dev::ArrayOutputCallbackData* data = - new ResourceArray_Dev::ArrayOutputCallbackData( - callback.output(), callback.pp_completion_callback()); - return get_interface<PPB_AudioInput_Dev_0_2>()->EnumerateDevices( - pp_resource(), &data->resource_array_output, - PP_MakeCompletionCallback( - &ResourceArray_Dev::ArrayOutputCallbackConverter, data)); + if (has_interface<PPB_AudioInput_Dev_0_3>()) { + return get_interface<PPB_AudioInput_Dev_0_3>()->EnumerateDevices( + pp_resource(), callback.output(), callback.pp_completion_callback()); + } + if (has_interface<PPB_AudioInput_Dev_0_2>()) { + if (!callback.pp_completion_callback().func) + return callback.MayForce(PP_ERROR_BLOCKS_MAIN_THREAD); + + // ArrayOutputCallbackConverter is responsible to delete it. + ResourceArray_Dev::ArrayOutputCallbackData* data = + new ResourceArray_Dev::ArrayOutputCallbackData( + callback.output(), callback.pp_completion_callback()); + return get_interface<PPB_AudioInput_Dev_0_2>()->EnumerateDevices( + pp_resource(), &data->resource_array_output, + PP_MakeCompletionCallback( + &ResourceArray_Dev::ArrayOutputCallbackConverter, data)); + } + + return callback.MayForce(PP_ERROR_NOINTERFACE); +} + +int32_t AudioInput_Dev::MonitorDeviceChange( + PP_MonitorDeviceChangeCallback callback, + void* user_data) { + if (has_interface<PPB_AudioInput_Dev_0_3>()) { + return get_interface<PPB_AudioInput_Dev_0_3>()->MonitorDeviceChange( + pp_resource(), callback, user_data); + } + + return PP_ERROR_NOINTERFACE; } int32_t AudioInput_Dev::Open(const DeviceRef_Dev& device_ref, @@ -60,6 +85,11 @@ int32_t AudioInput_Dev::Open(const DeviceRef_Dev& device_ref, PPB_AudioInput_Callback audio_input_callback, void* user_data, const CompletionCallback& callback) { + if (has_interface<PPB_AudioInput_Dev_0_3>()) { + return get_interface<PPB_AudioInput_Dev_0_3>()->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_2>()) { return get_interface<PPB_AudioInput_Dev_0_2>()->Open( pp_resource(), device_ref.pp_resource(), config.pp_resource(), @@ -70,6 +100,10 @@ int32_t AudioInput_Dev::Open(const DeviceRef_Dev& device_ref, } bool AudioInput_Dev::StartCapture() { + if (has_interface<PPB_AudioInput_Dev_0_3>()) { + return PP_ToBool(get_interface<PPB_AudioInput_Dev_0_3>()->StartCapture( + pp_resource())); + } if (has_interface<PPB_AudioInput_Dev_0_2>()) { return PP_ToBool(get_interface<PPB_AudioInput_Dev_0_2>()->StartCapture( pp_resource())); @@ -79,6 +113,10 @@ bool AudioInput_Dev::StartCapture() { } bool AudioInput_Dev::StopCapture() { + if (has_interface<PPB_AudioInput_Dev_0_3>()) { + return PP_ToBool(get_interface<PPB_AudioInput_Dev_0_3>()->StopCapture( + pp_resource())); + } if (has_interface<PPB_AudioInput_Dev_0_2>()) { return PP_ToBool(get_interface<PPB_AudioInput_Dev_0_2>()->StopCapture( pp_resource())); @@ -88,8 +126,11 @@ bool AudioInput_Dev::StopCapture() { } void AudioInput_Dev::Close() { - if (has_interface<PPB_AudioInput_Dev_0_2>()) + if (has_interface<PPB_AudioInput_Dev_0_3>()) { + get_interface<PPB_AudioInput_Dev_0_3>()->Close(pp_resource()); + } else if (has_interface<PPB_AudioInput_Dev_0_2>()) { get_interface<PPB_AudioInput_Dev_0_2>()->Close(pp_resource()); + } } } // namespace pp diff --git a/ppapi/cpp/dev/audio_input_dev.h b/ppapi/cpp/dev/audio_input_dev.h index fc336a9..22408cdf 100644 --- a/ppapi/cpp/dev/audio_input_dev.h +++ b/ppapi/cpp/dev/audio_input_dev.h @@ -37,6 +37,9 @@ class AudioInput_Dev : public Resource { const CompletionCallbackWithOutput<std::vector<DeviceRef_Dev> >& callback); + int32_t MonitorDeviceChange(PP_MonitorDeviceChangeCallback callback, + void* user_data); + /// If |device_ref| is null (i.e., is_null() returns true), the default device /// will be used. int32_t Open(const DeviceRef_Dev& device_ref, diff --git a/ppapi/examples/audio_input/audio_input.cc b/ppapi/examples/audio_input/audio_input.cc index 11efa63..e2df2b8 100644 --- a/ppapi/examples/audio_input/audio_input.cc +++ b/ppapi/examples/audio_input/audio_input.cc @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include <stdlib.h> #include <string.h> #include <algorithm> @@ -32,6 +33,7 @@ namespace { const PP_AudioSampleRate kSampleFrequency = PP_AUDIOSAMPLERATE_44100; const uint32_t kSampleCount = 1024; const uint32_t kChannelCount = 1; +const char* const kDelimiter = "#__#"; } // namespace @@ -48,6 +50,7 @@ class MyInstance : public pp::Instance { waiting_for_flush_completion_(false) { } virtual ~MyInstance() { + device_detector_.MonitorDeviceChange(NULL, NULL); audio_input_.Close(); delete[] samples_; @@ -62,7 +65,7 @@ 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); + device_detector_ = pp::AudioInput_Dev(this); // Try to ensure that we pick up a new set of samples between each // timer-generated repaint. @@ -88,10 +91,15 @@ class MyInstance : public pp::Instance { if (message_data.is_string()) { std::string event = message_data.AsString(); if (event == "PageInitialized") { + int32_t result = device_detector_.MonitorDeviceChange( + &MyInstance::MonitorDeviceChangeCallback, this); + if (result != PP_OK) + PostMessage(pp::Var("MonitorDeviceChangeFailed")); + pp::CompletionCallbackWithOutput<std::vector<pp::DeviceRef_Dev> > callback = callback_factory_.NewCallbackWithOutput( &MyInstance::EnumerateDevicesFinished); - int32_t result = audio_input_.EnumerateDevices(callback); + result = device_detector_.EnumerateDevices(callback); if (result != PP_OK_COMPLETIONPENDING) PostMessage(pp::Var("EnumerationFailed")); } else if (event == "UseDefault") { @@ -100,13 +108,20 @@ class MyInstance : public pp::Instance { Stop(); } else if (event == "Start") { Start(); - } - } 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(); + } else if (event.find("Monitor:") == 0) { + std::string index_str = event.substr(strlen("Monitor:")); + int index = atoi(index_str.c_str()); + if (index >= 0 && index < static_cast<int>(monitor_devices_.size())) + Open(monitor_devices_[index]); + else + PP_NOTREACHED(); + } else if (event.find("Enumerate:") == 0) { + std::string index_str = event.substr(strlen("Enumerate:")); + int index = atoi(index_str.c_str()); + if (index >= 0 && index < static_cast<int>(enumerate_devices_.size())) + Open(enumerate_devices_[index]); + else + PP_NOTREACHED(); } } } @@ -184,24 +199,10 @@ class MyInstance : public pp::Instance { return image; } - // TODO(viettrungluu): Danger! We really should lock, but which thread - // primitives to use? In any case, the |StopCapture()| in the destructor - // shouldn't return until this callback is done, so at least we should be - // writing to a valid region of memory. - static void CaptureCallback(const void* samples, - uint32_t num_bytes, - void* ctx) { - MyInstance* thiz = static_cast<MyInstance*>(ctx); - uint32_t buffer_size = - thiz->sample_count_ * thiz->channel_count_ * sizeof(int16_t); - PP_DCHECK(num_bytes <= buffer_size); - PP_DCHECK(num_bytes % (thiz->channel_count_ * sizeof(int16_t)) == 0); - memcpy(thiz->samples_, samples, num_bytes); - memset(reinterpret_cast<char*>(thiz->samples_) + num_bytes, 0, - buffer_size - num_bytes); - } - void Open(const pp::DeviceRef_Dev& device) { + audio_input_.Close(); + audio_input_ = pp::AudioInput_Dev(this); + pp::AudioConfig config = pp::AudioConfig(this, kSampleFrequency, sample_count_); @@ -225,13 +226,11 @@ class MyInstance : public pp::Instance { void EnumerateDevicesFinished(int32_t result, std::vector<pp::DeviceRef_Dev>& devices) { - static const char* const kDelimiter = "#__#"; - if (result == PP_OK) { - devices_.swap(devices); - std::string device_names; - for (size_t index = 0; index < devices_.size(); ++index) { - pp::Var name = devices_[index].GetName(); + enumerate_devices_.swap(devices); + std::string device_names = "Enumerate:"; + for (size_t index = 0; index < enumerate_devices_.size(); ++index) { + pp::Var name = enumerate_devices_[index].GetName(); PP_DCHECK(name.is_string()); if (index != 0) @@ -253,6 +252,43 @@ class MyInstance : public pp::Instance { } } + // TODO(viettrungluu): Danger! We really should lock, but which thread + // primitives to use? In any case, the |StopCapture()| in the destructor + // shouldn't return until this callback is done, so at least we should be + // writing to a valid region of memory. + static void CaptureCallback(const void* samples, + uint32_t num_bytes, + void* ctx) { + MyInstance* thiz = static_cast<MyInstance*>(ctx); + uint32_t buffer_size = + thiz->sample_count_ * thiz->channel_count_ * sizeof(int16_t); + PP_DCHECK(num_bytes <= buffer_size); + PP_DCHECK(num_bytes % (thiz->channel_count_ * sizeof(int16_t)) == 0); + memcpy(thiz->samples_, samples, num_bytes); + memset(reinterpret_cast<char*>(thiz->samples_) + num_bytes, 0, + buffer_size - num_bytes); + } + + static void MonitorDeviceChangeCallback(void* user_data, + uint32_t device_count, + const PP_Resource devices[]) { + MyInstance* thiz = static_cast<MyInstance*>(user_data); + + std::string device_names = "Monitor:"; + thiz->monitor_devices_.clear(); + thiz->monitor_devices_.reserve(device_count); + for (size_t index = 0; index < device_count; ++index) { + thiz->monitor_devices_.push_back(pp::DeviceRef_Dev(devices[index])); + pp::Var name = thiz->monitor_devices_.back().GetName(); + PP_DCHECK(name.is_string()); + + if (index != 0) + device_names += kDelimiter; + device_names += name.AsString(); + } + thiz->PostMessage(pp::Var(device_names)); + } + pp::CompletionCallbackFactory<MyInstance> callback_factory_; uint32_t sample_count_; @@ -267,9 +303,14 @@ class MyInstance : public pp::Instance { bool pending_paint_; bool waiting_for_flush_completion_; + // There is no need to have two resources to do capturing and device detecting + // separately. However, this makes the code of monitoring device change + // easier. pp::AudioInput_Dev audio_input_; + pp::AudioInput_Dev device_detector_; - std::vector<pp::DeviceRef_Dev> devices_; + std::vector<pp::DeviceRef_Dev> enumerate_devices_; + std::vector<pp::DeviceRef_Dev> monitor_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 8fa043c..2c764bb 100644 --- a/ppapi/examples/audio_input/audio_input.html +++ b/ppapi/examples/audio_input/audio_input.html @@ -8,13 +8,17 @@ <head> <title>Audio Input Example</title> <script type="text/javascript"> - var device_array = []; + var monitor_device_array = []; + var enumerate_device_array = []; + var monitor_notification_count = 0; 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 == 'MonitorDeviceChangeFailed') { + status.innerText = 'Monitor device change failed!'; } else if (message_event.data == 'OpenFailed') { status.innerText = 'Open device failed!'; } else if (message_event.data == 'StartFailed') { @@ -22,23 +26,60 @@ } 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); - } + AddDevices(message_event.data); } } } - function UseDesignatedDevice(index) { - UseDevice(device_array[index], index); + function AddDevices(command) { + var serialized_names = ''; + var is_monitor = false; + if (command.search('Monitor:') == 0) { + serialized_names = command.substr(8); + is_monitor = true; + monitor_notification_count++; + var counter = document.getElementById('notification_counter'); + counter.innerText = monitor_notification_count; + } else if (command.search('Enumerate:') == 0) { + serialized_names = command.substr(10); + } else { + status.innerText = 'Unrecognized command!'; + return; + } + + var storage = serialized_names.length != 0 ? + serialized_names.split('#__#') : []; + if (is_monitor) + monitor_device_array = storage; + else + enumerate_device_array = storage; + + var list = document.getElementById( + is_monitor ? 'monitor_list' : 'enumerate_list'); + while (list.firstChild) + list.removeChild(list.firstChild); + + for (var i = 0; i < storage.length; ++i) { + AppendDevice( + list, storage[i], + 'javascript:UseDesignatedDevice(' + is_monitor + ',' + i + ');'); + } + } + + function AppendDevice(list, text, href) { + var list_item = document.createElement('li'); + var link = document.createElement('a'); + link.href = href; + link.innerText = text; + list_item.appendChild(link); + list.appendChild(list_item); + } + + function UseDesignatedDevice(is_monitor, index) { + if (is_monitor) + UseDevice(monitor_device_array[index], 'Monitor:' + index); + else + UseDevice(enumerate_device_array[index], 'Enumerate:' + index); } function UseDefaultDevice() { @@ -50,12 +91,6 @@ in_use_device.innerText = display_text; var plugin = document.getElementById('plugin'); plugin.postMessage(command); - - var available_devices = document.getElementById('available_devices'); - available_devices.parentNode.removeChild(available_devices); - - var control_panel = document.getElementById('control_panel'); - control_panel.style.display = 'block'; } function Stop() { @@ -70,7 +105,7 @@ function Initialize() { var plugin = document.getElementById('plugin'); - plugin.addEventListener('message', HandleMessage, false); + plugin.addEventListener('message', HandleMessage, false) plugin.postMessage('PageInitialized'); } @@ -86,14 +121,26 @@ </div> <div id="available_devices"> Available device(s), choose one to open: - <ul id="device_list"> + <ul> <li><a href="javascript:UseDefaultDevice();"> - Default - use interface version 0.2 and NULL device ref</a></li> + Default - use NULL device ref</a></li> </ul> + <div> + <ul>List retrieved by MonitorDeviceChange(), will change when + pluging/unpluging devices: (Notifications received: + <span style="font-weight:bold" id="notification_counter">0</span> + )</ul> + <ul id="monitor_list"/> + </div> + <div> + <ul>List retrieved by EnumerateDevices(), never updated after the page is + initialized:</ul> + <ul id="enumerate_list"/> + </div> </div> - <div id="control_panel" style="display:none"> + <div id="control_panel"> <a href="javascript:Stop();">Stop</a> - <a href="javascript:Start();">Start</a> + <a href="javascript:Start();">Start</a> (known issue: crbug.com/161058) </div> <div id="status"></div> </body> diff --git a/ppapi/native_client/src/untrusted/pnacl_irt_shim/pnacl_shim.c b/ppapi/native_client/src/untrusted/pnacl_irt_shim/pnacl_shim.c index 8aba703..a8ccce0 100644 --- a/ppapi/native_client/src/untrusted/pnacl_irt_shim/pnacl_shim.c +++ b/ppapi/native_client/src/untrusted/pnacl_irt_shim/pnacl_shim.c @@ -169,6 +169,7 @@ static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_Graphics3DTrusted_1_0; static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_ImageDataTrusted_0_4; static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_URLLoaderTrusted_0_3; static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_AudioInput_Dev_0_2; +static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_AudioInput_Dev_0_3; static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_Buffer_Dev_0_4; static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_Console_Dev_0_1; static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_Crypto_Dev_0_1; @@ -1518,6 +1519,64 @@ void Pnacl_M19_PPB_AudioInput_Dev_Close(PP_Resource audio_input) { /* End wrapper methods for PPB_AudioInput_Dev_0_2 */ +/* Begin wrapper methods for PPB_AudioInput_Dev_0_3 */ + +static __attribute__((pnaclcall)) +PP_Resource Pnacl_M25_PPB_AudioInput_Dev_Create(PP_Instance instance) { + const struct PPB_AudioInput_Dev_0_3 *iface = Pnacl_WrapperInfo_PPB_AudioInput_Dev_0_3.real_iface; + return iface->Create(instance); +} + +static __attribute__((pnaclcall)) +PP_Bool Pnacl_M25_PPB_AudioInput_Dev_IsAudioInput(PP_Resource resource) { + const struct PPB_AudioInput_Dev_0_3 *iface = Pnacl_WrapperInfo_PPB_AudioInput_Dev_0_3.real_iface; + return iface->IsAudioInput(resource); +} + +static __attribute__((pnaclcall)) +int32_t Pnacl_M25_PPB_AudioInput_Dev_EnumerateDevices(PP_Resource audio_input, struct PP_ArrayOutput output, struct PP_CompletionCallback callback) { + const struct PPB_AudioInput_Dev_0_3 *iface = Pnacl_WrapperInfo_PPB_AudioInput_Dev_0_3.real_iface; + return iface->EnumerateDevices(audio_input, output, callback); +} + +static __attribute__((pnaclcall)) +int32_t Pnacl_M25_PPB_AudioInput_Dev_MonitorDeviceChange(PP_Resource audio_input, PP_MonitorDeviceChangeCallback callback, void* user_data) { + const struct PPB_AudioInput_Dev_0_3 *iface = Pnacl_WrapperInfo_PPB_AudioInput_Dev_0_3.real_iface; + return iface->MonitorDeviceChange(audio_input, callback, user_data); +} + +static __attribute__((pnaclcall)) +int32_t Pnacl_M25_PPB_AudioInput_Dev_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) { + const struct PPB_AudioInput_Dev_0_3 *iface = Pnacl_WrapperInfo_PPB_AudioInput_Dev_0_3.real_iface; + return iface->Open(audio_input, device_ref, config, audio_input_callback, user_data, callback); +} + +static __attribute__((pnaclcall)) +PP_Resource Pnacl_M25_PPB_AudioInput_Dev_GetCurrentConfig(PP_Resource audio_input) { + const struct PPB_AudioInput_Dev_0_3 *iface = Pnacl_WrapperInfo_PPB_AudioInput_Dev_0_3.real_iface; + return iface->GetCurrentConfig(audio_input); +} + +static __attribute__((pnaclcall)) +PP_Bool Pnacl_M25_PPB_AudioInput_Dev_StartCapture(PP_Resource audio_input) { + const struct PPB_AudioInput_Dev_0_3 *iface = Pnacl_WrapperInfo_PPB_AudioInput_Dev_0_3.real_iface; + return iface->StartCapture(audio_input); +} + +static __attribute__((pnaclcall)) +PP_Bool Pnacl_M25_PPB_AudioInput_Dev_StopCapture(PP_Resource audio_input) { + const struct PPB_AudioInput_Dev_0_3 *iface = Pnacl_WrapperInfo_PPB_AudioInput_Dev_0_3.real_iface; + return iface->StopCapture(audio_input); +} + +static __attribute__((pnaclcall)) +void Pnacl_M25_PPB_AudioInput_Dev_Close(PP_Resource audio_input) { + const struct PPB_AudioInput_Dev_0_3 *iface = Pnacl_WrapperInfo_PPB_AudioInput_Dev_0_3.real_iface; + iface->Close(audio_input); +} + +/* End wrapper methods for PPB_AudioInput_Dev_0_3 */ + /* Not generating wrapper methods for PPB_Buffer_Dev_0_4 */ /* Begin wrapper methods for PPB_Console_Dev_0_1 */ @@ -3694,6 +3753,18 @@ struct PPB_AudioInput_Dev_0_2 Pnacl_Wrappers_PPB_AudioInput_Dev_0_2 = { .Close = (void (*)(PP_Resource audio_input))&Pnacl_M19_PPB_AudioInput_Dev_Close }; +struct PPB_AudioInput_Dev_0_3 Pnacl_Wrappers_PPB_AudioInput_Dev_0_3 = { + .Create = (PP_Resource (*)(PP_Instance instance))&Pnacl_M25_PPB_AudioInput_Dev_Create, + .IsAudioInput = (PP_Bool (*)(PP_Resource resource))&Pnacl_M25_PPB_AudioInput_Dev_IsAudioInput, + .EnumerateDevices = (int32_t (*)(PP_Resource audio_input, struct PP_ArrayOutput output, struct PP_CompletionCallback callback))&Pnacl_M25_PPB_AudioInput_Dev_EnumerateDevices, + .MonitorDeviceChange = (int32_t (*)(PP_Resource audio_input, PP_MonitorDeviceChangeCallback callback, void* user_data))&Pnacl_M25_PPB_AudioInput_Dev_MonitorDeviceChange, + .Open = (int32_t (*)(PP_Resource audio_input, PP_Resource device_ref, PP_Resource config, PPB_AudioInput_Callback audio_input_callback, void* user_data, struct PP_CompletionCallback callback))&Pnacl_M25_PPB_AudioInput_Dev_Open, + .GetCurrentConfig = (PP_Resource (*)(PP_Resource audio_input))&Pnacl_M25_PPB_AudioInput_Dev_GetCurrentConfig, + .StartCapture = (PP_Bool (*)(PP_Resource audio_input))&Pnacl_M25_PPB_AudioInput_Dev_StartCapture, + .StopCapture = (PP_Bool (*)(PP_Resource audio_input))&Pnacl_M25_PPB_AudioInput_Dev_StopCapture, + .Close = (void (*)(PP_Resource audio_input))&Pnacl_M25_PPB_AudioInput_Dev_Close +}; + /* Not generating wrapper interface for PPB_Buffer_Dev_0_4 */ struct PPB_Console_Dev_0_1 Pnacl_Wrappers_PPB_Console_Dev_0_1 = { @@ -4472,6 +4543,12 @@ static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_AudioInput_Dev_0_2 = { .real_iface = NULL }; +static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_AudioInput_Dev_0_3 = { + .iface_macro = PPB_AUDIO_INPUT_DEV_INTERFACE_0_3, + .wrapped_iface = (void *) &Pnacl_Wrappers_PPB_AudioInput_Dev_0_3, + .real_iface = NULL +}; + static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_Buffer_Dev_0_4 = { .iface_macro = PPB_BUFFER_DEV_INTERFACE_0_4, .wrapped_iface = NULL /* Still need slot for real_iface */, @@ -5004,6 +5081,7 @@ static struct __PnaclWrapperInfo *s_ppb_wrappers[] = { &Pnacl_WrapperInfo_PPB_ImageDataTrusted_0_4, &Pnacl_WrapperInfo_PPB_URLLoaderTrusted_0_3, &Pnacl_WrapperInfo_PPB_AudioInput_Dev_0_2, + &Pnacl_WrapperInfo_PPB_AudioInput_Dev_0_3, &Pnacl_WrapperInfo_PPB_Buffer_Dev_0_4, &Pnacl_WrapperInfo_PPB_Console_Dev_0_1, &Pnacl_WrapperInfo_PPB_Crypto_Dev_0_1, diff --git a/ppapi/ppapi_proxy.gypi b/ppapi/ppapi_proxy.gypi index d0c9271..81ec94f 100644 --- a/ppapi/ppapi_proxy.gypi +++ b/ppapi/ppapi_proxy.gypi @@ -26,6 +26,8 @@ 'proxy/browser_font_resource_trusted.cc', 'proxy/browser_font_resource_trusted.h', 'proxy/connection.h', + 'proxy/device_enumeration_resource_helper.cc', + 'proxy/device_enumeration_resource_helper.h', 'proxy/dispatcher.cc', 'proxy/dispatcher.h', 'proxy/enter_proxy.h', @@ -184,6 +186,7 @@ 'proxy/audio_input_resource.cc', 'proxy/broker_dispatcher.cc', 'proxy/browser_font_resource_trusted.cc', + 'proxy/device_enumeration_resource_helper.cc', 'proxy/flash_clipboard_resource.cc', 'proxy/flash_device_id_resource.cc', 'proxy/flash_file_resource.cc', diff --git a/ppapi/ppapi_tests.gypi b/ppapi/ppapi_tests.gypi index 58f7bf6..c989d9c 100644 --- a/ppapi/ppapi_tests.gypi +++ b/ppapi/ppapi_tests.gypi @@ -142,6 +142,7 @@ 'proxy/run_all_unittests.cc', 'host/resource_message_filter_unittest.cc', + 'proxy/device_enumeration_resource_helper_unittest.cc', 'proxy/file_chooser_resource_unittest.cc', 'proxy/flash_resource_unittest.cc', 'proxy/mock_resource.cc', diff --git a/ppapi/proxy/audio_input_resource.cc b/ppapi/proxy/audio_input_resource.cc index dd13b57..2f17c6c 100644 --- a/ppapi/proxy/audio_input_resource.cc +++ b/ppapi/proxy/audio_input_resource.cc @@ -14,7 +14,6 @@ #include "ppapi/proxy/resource_message_params.h" #include "ppapi/proxy/serialized_structs.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/shared_impl/tracked_callback.h" #include "ppapi/thunk/enter.h" @@ -32,7 +31,7 @@ AudioInputResource::AudioInputResource( shared_memory_size_(0), audio_input_callback_(NULL), user_data_(NULL), - pending_enumerate_devices_(false) { + ALLOW_THIS_IN_INITIALIZER_LIST(enumeration_helper_(this)) { SendCreate(RENDERER, PpapiHostMsg_AudioInput_Create()); } @@ -44,21 +43,29 @@ thunk::PPB_AudioInput_API* AudioInputResource::AsPPB_AudioInput_API() { return this; } -int32_t AudioInputResource::EnumerateDevices( +void AudioInputResource::OnReplyReceived( + const ResourceMessageReplyParams& params, + const IPC::Message& msg) { + if (!enumeration_helper_.HandleReply(params, msg)) + PluginResource::OnReplyReceived(params, msg); +} + +int32_t AudioInputResource::EnumerateDevices0_2( PP_Resource* devices, scoped_refptr<TrackedCallback> callback) { - if (pending_enumerate_devices_) - return PP_ERROR_INPROGRESS; - if (!devices) - return PP_ERROR_BADARGUMENT; + return enumeration_helper_.EnumerateDevices0_2(devices, callback); +} - pending_enumerate_devices_ = true; - PpapiHostMsg_AudioInput_EnumerateDevices msg; - Call<PpapiPluginMsg_AudioInput_EnumerateDevicesReply>( - RENDERER, msg, - base::Bind(&AudioInputResource::OnPluginMsgEnumerateDevicesReply, - base::Unretained(this), devices, callback)); - return PP_OK_COMPLETIONPENDING; +int32_t AudioInputResource::EnumerateDevices( + const PP_ArrayOutput& output, + scoped_refptr<TrackedCallback> callback) { + return enumeration_helper_.EnumerateDevices(output, callback); +} + +int32_t AudioInputResource::MonitorDeviceChange( + PP_MonitorDeviceChangeCallback callback, + void* user_data) { + return enumeration_helper_.MonitorDeviceChange(callback, user_data); } int32_t AudioInputResource::Open(const std::string& device_id, @@ -153,25 +160,8 @@ void AudioInputResource::Close() { open_callback_->PostAbort(); } -void AudioInputResource::OnPluginMsgEnumerateDevicesReply( - PP_Resource* devices_resource, - scoped_refptr<TrackedCallback> callback, - const ResourceMessageReplyParams& params, - const std::vector<DeviceRefData>& devices) { - pending_enumerate_devices_ = false; - - // We shouldn't access |devices_resource| if the callback has been called, - // which is possible if the last plugin reference to this resource has gone - // away, and the callback has been aborted. - if (!TrackedCallback::IsPending(callback)) - return; - - if (params.result() == PP_OK) { - *devices_resource = PPB_DeviceRef_Shared::CreateResourceArray( - OBJECT_IS_PROXY, pp_instance(), devices); - } - - callback->Run(params.result()); +void AudioInputResource::LastPluginRefWasDeleted() { + enumeration_helper_.LastPluginRefWasDeleted(); } void AudioInputResource::OnPluginMsgOpenReply( diff --git a/ppapi/proxy/audio_input_resource.h b/ppapi/proxy/audio_input_resource.h index 7ea91b6..e8a1ffd 100644 --- a/ppapi/proxy/audio_input_resource.h +++ b/ppapi/proxy/audio_input_resource.h @@ -5,8 +5,6 @@ #ifndef PPAPI_PROXY_AUDIO_INPUT_RESOURCE_H_ #define PPAPI_PROXY_AUDIO_INPUT_RESOURCE_H_ -#include <vector> - #include "base/basictypes.h" #include "base/compiler_specific.h" #include "base/memory/ref_counted.h" @@ -14,14 +12,12 @@ #include "base/shared_memory.h" #include "base/sync_socket.h" #include "base/threading/simple_thread.h" +#include "ppapi/proxy/device_enumeration_resource_helper.h" #include "ppapi/proxy/plugin_resource.h" #include "ppapi/shared_impl/scoped_pp_resource.h" #include "ppapi/thunk/ppb_audio_input_api.h" namespace ppapi { - -struct DeviceRefData; - namespace proxy { class ResourceMessageReplyParams; @@ -36,11 +32,19 @@ class AudioInputResource : public PluginResource, // Resource overrides. virtual thunk::PPB_AudioInput_API* AsPPB_AudioInput_API() OVERRIDE; + virtual void OnReplyReceived(const ResourceMessageReplyParams& params, + const IPC::Message& msg) OVERRIDE; // PPB_AudioInput_API implementation. - virtual int32_t EnumerateDevices( + virtual int32_t EnumerateDevices0_2( PP_Resource* devices, scoped_refptr<TrackedCallback> callback) OVERRIDE; + virtual int32_t EnumerateDevices( + const PP_ArrayOutput& output, + scoped_refptr<TrackedCallback> callback) OVERRIDE; + virtual int32_t MonitorDeviceChange( + PP_MonitorDeviceChangeCallback callback, + void* user_data) OVERRIDE; virtual int32_t Open(const std::string& device_id, PP_Resource config, PPB_AudioInput_Callback audio_input_callback, @@ -51,6 +55,10 @@ class AudioInputResource : public PluginResource, virtual PP_Bool StopCapture() OVERRIDE; virtual void Close() OVERRIDE; + protected: + // Resource override. + virtual void LastPluginRefWasDeleted() OVERRIDE; + private: enum OpenState { BEFORE_OPEN, @@ -58,11 +66,6 @@ class AudioInputResource : public PluginResource, CLOSED }; - void OnPluginMsgEnumerateDevicesReply( - PP_Resource* devices_resource, - scoped_refptr<TrackedCallback> callback, - const ResourceMessageReplyParams& params, - const std::vector<DeviceRefData>& devices); void OnPluginMsgOpenReply(const ResourceMessageReplyParams& params); // Sets the shared memory and socket handles. This will automatically start @@ -107,7 +110,6 @@ class AudioInputResource : public PluginResource, // User data pointer passed verbatim to the callback function. void* user_data_; - bool pending_enumerate_devices_; // The callback is not directly passed to OnPluginMsgOpenReply() because we // would like to be able to cancel it early in Close(). scoped_refptr<TrackedCallback> open_callback_; @@ -116,6 +118,8 @@ class AudioInputResource : public PluginResource, // we just dish it out as requested by the plugin. ScopedPPResource config_; + DeviceEnumerationResourceHelper enumeration_helper_; + DISALLOW_COPY_AND_ASSIGN(AudioInputResource); }; diff --git a/ppapi/proxy/device_enumeration_resource_helper.cc b/ppapi/proxy/device_enumeration_resource_helper.cc new file mode 100644 index 0000000..2796439 --- /dev/null +++ b/ppapi/proxy/device_enumeration_resource_helper.cc @@ -0,0 +1,200 @@ +// 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/proxy/device_enumeration_resource_helper.h" + +#include "base/bind.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "ipc/ipc_message.h" +#include "ipc/ipc_message_macros.h" +#include "ppapi/c/pp_array_output.h" +#include "ppapi/c/pp_errors.h" +#include "ppapi/proxy/dispatch_reply_message.h" +#include "ppapi/proxy/plugin_resource.h" +#include "ppapi/proxy/ppapi_messages.h" +#include "ppapi/proxy/resource_message_params.h" +#include "ppapi/shared_impl/array_writer.h" +#include "ppapi/shared_impl/ppapi_globals.h" +#include "ppapi/shared_impl/ppb_device_ref_shared.h" +#include "ppapi/shared_impl/proxy_lock.h" +#include "ppapi/shared_impl/resource_tracker.h" +#include "ppapi/shared_impl/tracked_callback.h" + +namespace ppapi { +namespace proxy { + +DeviceEnumerationResourceHelper::DeviceEnumerationResourceHelper( + PluginResource* owner) + : owner_(owner), + pending_enumerate_devices_(false), + monitor_callback_id_(0), + monitor_callback_(NULL), + monitor_user_data_(NULL) { +} + +DeviceEnumerationResourceHelper::~DeviceEnumerationResourceHelper() { +} + +int32_t DeviceEnumerationResourceHelper::EnumerateDevices0_2( + PP_Resource* devices, + scoped_refptr<TrackedCallback> callback) { + if (pending_enumerate_devices_) + return PP_ERROR_INPROGRESS; + if (!devices) + return PP_ERROR_BADARGUMENT; + + pending_enumerate_devices_ = true; + PpapiHostMsg_DeviceEnumeration_EnumerateDevices msg; + owner_->Call<PpapiPluginMsg_DeviceEnumeration_EnumerateDevicesReply>( + PluginResource::RENDERER, msg, + base::Bind( + &DeviceEnumerationResourceHelper::OnPluginMsgEnumerateDevicesReply0_2, + AsWeakPtr(), devices, callback)); + return PP_OK_COMPLETIONPENDING; +} + +int32_t DeviceEnumerationResourceHelper::EnumerateDevices( + const PP_ArrayOutput& output, + scoped_refptr<TrackedCallback> callback) { + if (pending_enumerate_devices_) + return PP_ERROR_INPROGRESS; + + pending_enumerate_devices_ = true; + PpapiHostMsg_DeviceEnumeration_EnumerateDevices msg; + owner_->Call<PpapiPluginMsg_DeviceEnumeration_EnumerateDevicesReply>( + PluginResource::RENDERER, msg, + base::Bind( + &DeviceEnumerationResourceHelper::OnPluginMsgEnumerateDevicesReply, + AsWeakPtr(), output, callback)); + return PP_OK_COMPLETIONPENDING; +} + +int32_t DeviceEnumerationResourceHelper::MonitorDeviceChange( + PP_MonitorDeviceChangeCallback callback, + void* user_data) { + monitor_callback_id_++; + monitor_callback_ = callback; + monitor_user_data_ = user_data; + + if (callback) { + owner_->Post(PluginResource::RENDERER, + PpapiHostMsg_DeviceEnumeration_MonitorDeviceChange( + monitor_callback_id_)); + } else { + owner_->Post(PluginResource::RENDERER, + PpapiHostMsg_DeviceEnumeration_StopMonitoringDeviceChange()); + } + return PP_OK; +} + +bool DeviceEnumerationResourceHelper::HandleReply( + const ResourceMessageReplyParams& params, + const IPC::Message& msg) { + IPC_BEGIN_MESSAGE_MAP(DeviceEnumerationResourceHelper, msg) + PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL( + PpapiPluginMsg_DeviceEnumeration_NotifyDeviceChange, + OnPluginMsgNotifyDeviceChange) + PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL_UNHANDLED(return false) + IPC_END_MESSAGE_MAP() + + return true; +} + +void DeviceEnumerationResourceHelper::LastPluginRefWasDeleted() { + // Make sure that no further notifications are sent to the plugin. + monitor_callback_id_++; + monitor_callback_ = NULL; + monitor_user_data_ = NULL; + + // There is no need to do anything with pending callback of + // EnumerateDevices(), because OnPluginMsgEnumerateDevicesReply*() will handle + // that properly. +} + +void DeviceEnumerationResourceHelper::OnPluginMsgEnumerateDevicesReply0_2( + PP_Resource* devices_resource, + scoped_refptr<TrackedCallback> callback, + const ResourceMessageReplyParams& params, + const std::vector<DeviceRefData>& devices) { + pending_enumerate_devices_ = false; + + // We shouldn't access |devices_resource| if the callback has been called, + // which is possible if the last plugin reference to the corresponding + // resource has gone away, and the callback has been aborted. + if (!TrackedCallback::IsPending(callback)) + return; + + if (params.result() == PP_OK) { + *devices_resource = PPB_DeviceRef_Shared::CreateResourceArray( + OBJECT_IS_PROXY, owner_->pp_instance(), devices); + } + + callback->Run(params.result()); +} + +void DeviceEnumerationResourceHelper::OnPluginMsgEnumerateDevicesReply( + const PP_ArrayOutput& output, + scoped_refptr<TrackedCallback> callback, + const ResourceMessageReplyParams& params, + const std::vector<DeviceRefData>& devices) { + pending_enumerate_devices_ = false; + + // We shouldn't access |output| if the callback has been called, which is + // possible if the last plugin reference to the corresponding resource has + // gone away, and the callback has been aborted. + if (!TrackedCallback::IsPending(callback)) + return; + + int32_t result = params.result(); + if (result == PP_OK) { + ArrayWriter writer(output); + if (writer.is_valid()) { + std::vector<scoped_refptr<Resource> > device_resources; + for (size_t i = 0; i < devices.size(); ++i) { + device_resources.push_back(new PPB_DeviceRef_Shared( + OBJECT_IS_PROXY, owner_->pp_instance(), devices[i])); + } + if (!writer.StoreResourceVector(device_resources)) + result = PP_ERROR_FAILED; + } else { + result = PP_ERROR_BADARGUMENT; + } + } + + callback->Run(params.result()); +} + +void DeviceEnumerationResourceHelper::OnPluginMsgNotifyDeviceChange( + const ResourceMessageReplyParams& /* params */, + uint32_t callback_id, + const std::vector<DeviceRefData>& devices) { + if (monitor_callback_id_ != callback_id) { + // A new callback or NULL has been set. + return; + } + + CHECK(monitor_callback_); + + scoped_array<PP_Resource> elements; + uint32_t size = devices.size(); + if (size > 0) { + elements.reset(new PP_Resource[size]); + for (size_t index = 0; index < size; ++index) { + PPB_DeviceRef_Shared* device_object = new PPB_DeviceRef_Shared( + OBJECT_IS_PROXY, owner_->pp_instance(), devices[index]); + elements[index] = device_object->GetReference(); + } + } + + // TODO(yzshen): make sure |monitor_callback_| is called on the same thread as + // the one on which MonitorDeviceChange() is called. + CallWhileUnlocked(base::Bind(monitor_callback_, monitor_user_data_, size, + elements.get())); + for (size_t index = 0; index < size; ++index) + PpapiGlobals::Get()->GetResourceTracker()->ReleaseResource(elements[index]); +} + +} // namespace proxy +} // namespace ppapi diff --git a/ppapi/proxy/device_enumeration_resource_helper.h b/ppapi/proxy/device_enumeration_resource_helper.h new file mode 100644 index 0000000..744a0eb --- /dev/null +++ b/ppapi/proxy/device_enumeration_resource_helper.h @@ -0,0 +1,82 @@ +// 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_PROXY_DEVICE_ENUMERATION_RESOURCE_HELPER_H_ +#define PPAPI_PROXY_DEVICE_ENUMERATION_RESOURCE_HELPER_H_ + +#include <vector> + +#include "base/basictypes.h" +#include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" +#include "ppapi/c/dev/ppb_device_ref_dev.h" +#include "ppapi/proxy/ppapi_proxy_export.h" + +namespace IPC { +class Message; +} + +struct PP_ArrayOutput; + +namespace ppapi { + +struct DeviceRefData; +class TrackedCallback; + +namespace proxy { + +class PluginResource; +class ResourceMessageReplyParams; + +class PPAPI_PROXY_EXPORT DeviceEnumerationResourceHelper + : public base::SupportsWeakPtr<DeviceEnumerationResourceHelper> { + public: + // |owner| must outlive this object. + explicit DeviceEnumerationResourceHelper(PluginResource* owner); + ~DeviceEnumerationResourceHelper(); + + int32_t EnumerateDevices0_2(PP_Resource* devices, + scoped_refptr<TrackedCallback> callback); + int32_t EnumerateDevices(const PP_ArrayOutput& output, + scoped_refptr<TrackedCallback> callback); + int32_t MonitorDeviceChange(PP_MonitorDeviceChangeCallback callback, + void* user_data); + + // Returns true if the message has been handled. + bool HandleReply(const ResourceMessageReplyParams& params, + const IPC::Message& msg); + + void LastPluginRefWasDeleted(); + + private: + void OnPluginMsgEnumerateDevicesReply0_2( + PP_Resource* devices_resource, + scoped_refptr<TrackedCallback> callback, + const ResourceMessageReplyParams& params, + const std::vector<DeviceRefData>& devices); + void OnPluginMsgEnumerateDevicesReply( + const PP_ArrayOutput& output, + scoped_refptr<TrackedCallback> callback, + const ResourceMessageReplyParams& params, + const std::vector<DeviceRefData>& devices); + void OnPluginMsgNotifyDeviceChange(const ResourceMessageReplyParams& params, + uint32_t callback_id, + const std::vector<DeviceRefData>& devices); + + // Not owned by this object. + PluginResource* owner_; + + bool pending_enumerate_devices_; + + uint32_t monitor_callback_id_; + PP_MonitorDeviceChangeCallback monitor_callback_; + void* monitor_user_data_; + + DISALLOW_COPY_AND_ASSIGN(DeviceEnumerationResourceHelper); +}; + +} // namespace proxy +} // namespace ppapi + +#endif // PPAPI_PROXY_DEVICE_ENUMERATION_RESOURCE_HELPER_H_ diff --git a/ppapi/proxy/device_enumeration_resource_helper_unittest.cc b/ppapi/proxy/device_enumeration_resource_helper_unittest.cc new file mode 100644 index 0000000..330bca3 --- /dev/null +++ b/ppapi/proxy/device_enumeration_resource_helper_unittest.cc @@ -0,0 +1,386 @@ +// 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 "base/basictypes.h" +#include "base/compiler_specific.h" +#include "ppapi/c/pp_errors.h" +#include "ppapi/proxy/connection.h" +#include "ppapi/proxy/device_enumeration_resource_helper.h" +#include "ppapi/proxy/plugin_resource.h" +#include "ppapi/proxy/plugin_resource_tracker.h" +#include "ppapi/proxy/plugin_var_tracker.h" +#include "ppapi/proxy/ppapi_message_utils.h" +#include "ppapi/proxy/ppapi_messages.h" +#include "ppapi/proxy/ppapi_proxy_test.h" +#include "ppapi/shared_impl/ppb_device_ref_shared.h" +#include "ppapi/shared_impl/var.h" +#include "ppapi/thunk/enter.h" +#include "ppapi/thunk/ppb_device_ref_api.h" +#include "ppapi/thunk/thunk.h" + +namespace ppapi { +namespace proxy { + +namespace { + +typedef PluginProxyTest DeviceEnumerationResourceHelperTest; + +Connection GetConnection(PluginProxyTestHarness* harness) { + CHECK(harness->GetGlobals()->IsPluginGlobals()); + + return Connection( + static_cast<PluginGlobals*>(harness->GetGlobals())->GetBrowserSender(), + harness->plugin_dispatcher()); +} + +bool CompareDeviceRef(PluginVarTracker* var_tracker, + PP_Resource resource, + const DeviceRefData& expected) { + thunk::EnterResource<thunk::PPB_DeviceRef_API> enter(resource, true); + if (enter.failed()) + return false; + + if (expected.type != enter.object()->GetType()) + return false; + + PP_Var name_pp_var = enter.object()->GetName(); + bool result = false; + do { + Var* name_var = var_tracker->GetVar(name_pp_var); + if (!name_var) + break; + StringVar* name_string_var = name_var->AsStringVar(); + if (!name_string_var) + break; + if (expected.name != name_string_var->value()) + break; + + result = true; + } while (false); + var_tracker->ReleaseVar(name_pp_var); + return result; +} + +class TestResource : public PluginResource { + public: + TestResource(Connection connection, PP_Instance instance) + : PluginResource(connection, instance), + ALLOW_THIS_IN_INITIALIZER_LIST(device_enumeration_(this)) { + } + + virtual ~TestResource() {} + + virtual void OnReplyReceived(const ResourceMessageReplyParams& params, + const IPC::Message& msg) OVERRIDE { + if (!device_enumeration_.HandleReply(params, msg)) + PluginResource::OnReplyReceived(params, msg); + } + + DeviceEnumerationResourceHelper& device_enumeration() { + return device_enumeration_; + } + + private: + DeviceEnumerationResourceHelper device_enumeration_; + + DISALLOW_COPY_AND_ASSIGN(TestResource); +}; + +class TestCallback { + public: + TestCallback() : called_(false), result_(PP_ERROR_FAILED) { + } + ~TestCallback() { + CHECK(called_); + } + + PP_CompletionCallback MakeCompletionCallback() { + return PP_MakeCompletionCallback(&CompletionCallbackBody, this); + } + + bool called() const { return called_; } + int32_t result() const { return result_; } + + private: + static void CompletionCallbackBody(void* user_data, int32_t result) { + TestCallback* callback = static_cast<TestCallback*>(user_data); + + CHECK(!callback->called_); + callback->called_ = true; + callback->result_ = result; + } + + bool called_; + int32_t result_; + + DISALLOW_COPY_AND_ASSIGN(TestCallback); +}; + +class TestArrayOutput { + public: + explicit TestArrayOutput(PluginResourceTracker* resource_tracker) + : data_(NULL), + count_(0), + resource_tracker_(resource_tracker) { + } + + ~TestArrayOutput() { + if (count_ > 0) { + for (size_t i = 0; i < count_; ++i) + resource_tracker_->ReleaseResource(data_[i]); + delete [] data_; + } + } + + PP_ArrayOutput MakeArrayOutput() { + PP_ArrayOutput array_output = { &GetDataBuffer, this }; + return array_output; + } + + const PP_Resource* data() const { return data_; } + uint32_t count() const { return count_; } + + private: + static void* GetDataBuffer(void* user_data, + uint32_t element_count, + uint32_t element_size) { + CHECK_EQ(element_size, sizeof(PP_Resource)); + + TestArrayOutput* output = static_cast<TestArrayOutput*>(user_data); + CHECK(!output->data_); + + output->count_ = element_count; + if (element_count > 0) + output->data_ = new PP_Resource[element_count]; + else + output->data_ = NULL; + + return output->data_; + } + + PP_Resource* data_; + uint32_t count_; + PluginResourceTracker* resource_tracker_; + + DISALLOW_COPY_AND_ASSIGN(TestArrayOutput); +}; + +class TestMonitorDeviceChange { + public: + explicit TestMonitorDeviceChange(PluginVarTracker* var_tracker) + : called_(false), + same_as_expected_(false), + var_tracker_(var_tracker) { + } + + ~TestMonitorDeviceChange() {} + + void SetExpectedResult(const std::vector<DeviceRefData>& expected) { + called_ = false; + same_as_expected_ = false; + expected_ = expected; + } + + bool called() const { return called_; } + + bool same_as_expected() const { return same_as_expected_; } + + static void MonitorDeviceChangeCallback(void* user_data, + uint32_t device_count, + const PP_Resource devices[]) { + TestMonitorDeviceChange* helper = + static_cast<TestMonitorDeviceChange*>(user_data); + CHECK(!helper->called_); + + helper->called_ = true; + helper->same_as_expected_ = false; + if (device_count != helper->expected_.size()) + return; + for (size_t i = 0; i < device_count; ++i) { + if (!CompareDeviceRef(helper->var_tracker_, devices[i], + helper->expected_[i])) { + return; + } + } + helper->same_as_expected_ = true; + } + + private: + bool called_; + bool same_as_expected_; + std::vector<DeviceRefData> expected_; + PluginVarTracker* var_tracker_; + + DISALLOW_COPY_AND_ASSIGN(TestMonitorDeviceChange); +}; + +} // namespace + +TEST_F(DeviceEnumerationResourceHelperTest, EnumerateDevices) { + scoped_refptr<TestResource> resource( + new TestResource(GetConnection(this), pp_instance())); + DeviceEnumerationResourceHelper& device_enumeration = + resource->device_enumeration(); + + TestArrayOutput output(&resource_tracker()); + TestCallback callback; + scoped_refptr<TrackedCallback> tracked_callback( + new TrackedCallback(resource.get(), callback.MakeCompletionCallback())); + int32_t result = device_enumeration.EnumerateDevices(output.MakeArrayOutput(), + tracked_callback); + ASSERT_EQ(PP_OK_COMPLETIONPENDING, result); + + // Should have sent an EnumerateDevices message. + ResourceMessageCallParams params; + IPC::Message msg; + ASSERT_TRUE(sink().GetFirstResourceCallMatching( + PpapiHostMsg_DeviceEnumeration_EnumerateDevices::ID, ¶ms, &msg)); + + // Synthesize a response. + ResourceMessageReplyParams reply_params(params.pp_resource(), + params.sequence()); + reply_params.set_result(PP_OK); + std::vector<DeviceRefData> data; + DeviceRefData data_item; + data_item.type = PP_DEVICETYPE_DEV_AUDIOCAPTURE; + data_item.name = "name_1"; + data_item.id = "id_1"; + data.push_back(data_item); + data_item.type = PP_DEVICETYPE_DEV_VIDEOCAPTURE; + data_item.name = "name_2"; + data_item.id = "id_2"; + data.push_back(data_item); + + ASSERT_TRUE(plugin_dispatcher()->OnMessageReceived( + PpapiPluginMsg_ResourceReply( + reply_params, + PpapiPluginMsg_DeviceEnumeration_EnumerateDevicesReply(data)))); + + EXPECT_TRUE(callback.called()); + EXPECT_EQ(PP_OK, callback.result()); + EXPECT_EQ(2U, output.count()); + for (size_t i = 0; i < output.count(); ++i) + EXPECT_TRUE(CompareDeviceRef(&var_tracker(), output.data()[i], data[i])); +} + +TEST_F(DeviceEnumerationResourceHelperTest, MonitorDeviceChange) { + scoped_refptr<TestResource> resource( + new TestResource(GetConnection(this), pp_instance())); + DeviceEnumerationResourceHelper& device_enumeration = + resource->device_enumeration(); + + TestMonitorDeviceChange helper(&var_tracker()); + + int32_t result = device_enumeration.MonitorDeviceChange( + &TestMonitorDeviceChange::MonitorDeviceChangeCallback, &helper); + ASSERT_EQ(PP_OK, result); + + // Should have sent a MonitorDeviceChange message. + ResourceMessageCallParams params; + IPC::Message msg; + ASSERT_TRUE(sink().GetFirstResourceCallMatching( + PpapiHostMsg_DeviceEnumeration_MonitorDeviceChange::ID, ¶ms, &msg)); + sink().ClearMessages(); + + uint32_t callback_id = 0; + ASSERT_TRUE(UnpackMessage<PpapiHostMsg_DeviceEnumeration_MonitorDeviceChange>( + msg, &callback_id)); + + ResourceMessageReplyParams reply_params(params.pp_resource(), 0); + reply_params.set_result(PP_OK); + std::vector<DeviceRefData> data; + + helper.SetExpectedResult(data); + + // Synthesize a response with no device. + ASSERT_TRUE(plugin_dispatcher()->OnMessageReceived( + PpapiPluginMsg_ResourceReply( + reply_params, + PpapiPluginMsg_DeviceEnumeration_NotifyDeviceChange( + callback_id, data)))); + EXPECT_TRUE(helper.called() && helper.same_as_expected()); + + DeviceRefData data_item; + data_item.type = PP_DEVICETYPE_DEV_AUDIOCAPTURE; + data_item.name = "name_1"; + data_item.id = "id_1"; + data.push_back(data_item); + data_item.type = PP_DEVICETYPE_DEV_VIDEOCAPTURE; + data_item.name = "name_2"; + data_item.id = "id_2"; + data.push_back(data_item); + + helper.SetExpectedResult(data); + + // Synthesize a response with some devices. + ASSERT_TRUE(plugin_dispatcher()->OnMessageReceived( + PpapiPluginMsg_ResourceReply( + reply_params, + PpapiPluginMsg_DeviceEnumeration_NotifyDeviceChange( + callback_id, data)))); + EXPECT_TRUE(helper.called() && helper.same_as_expected()); + + TestMonitorDeviceChange helper2(&var_tracker()); + + result = device_enumeration.MonitorDeviceChange( + &TestMonitorDeviceChange::MonitorDeviceChangeCallback, &helper2); + ASSERT_EQ(PP_OK, result); + + // Should have sent another MonitorDeviceChange message. + ResourceMessageCallParams params2; + IPC::Message msg2; + ASSERT_TRUE(sink().GetFirstResourceCallMatching( + PpapiHostMsg_DeviceEnumeration_MonitorDeviceChange::ID, ¶ms2, &msg2)); + sink().ClearMessages(); + + uint32_t callback_id2 = 0; + ASSERT_TRUE(UnpackMessage<PpapiHostMsg_DeviceEnumeration_MonitorDeviceChange>( + msg2, &callback_id2)); + + helper.SetExpectedResult(data); + helper2.SetExpectedResult(data); + // |helper2| should receive the result while |helper| shouldn't. + ASSERT_TRUE(plugin_dispatcher()->OnMessageReceived( + PpapiPluginMsg_ResourceReply( + reply_params, + PpapiPluginMsg_DeviceEnumeration_NotifyDeviceChange( + callback_id2, data)))); + EXPECT_TRUE(helper2.called() && helper2.same_as_expected()); + EXPECT_FALSE(helper.called()); + + helper.SetExpectedResult(data); + helper2.SetExpectedResult(data); + // Even if a message with |callback_id| arrives. |helper| shouldn't receive + // the result. + ASSERT_TRUE(plugin_dispatcher()->OnMessageReceived( + PpapiPluginMsg_ResourceReply( + reply_params, + PpapiPluginMsg_DeviceEnumeration_NotifyDeviceChange( + callback_id, data)))); + EXPECT_FALSE(helper2.called()); + EXPECT_FALSE(helper.called()); + + result = device_enumeration.MonitorDeviceChange(NULL, NULL); + ASSERT_EQ(PP_OK, result); + + // Should have sent a StopMonitoringDeviceChange message. + ResourceMessageCallParams params3; + IPC::Message msg3; + ASSERT_TRUE(sink().GetFirstResourceCallMatching( + PpapiHostMsg_DeviceEnumeration_StopMonitoringDeviceChange::ID, + ¶ms3, &msg3)); + sink().ClearMessages(); + + helper2.SetExpectedResult(data); + // |helper2| shouldn't receive any result any more. + ASSERT_TRUE(plugin_dispatcher()->OnMessageReceived( + PpapiPluginMsg_ResourceReply( + reply_params, + PpapiPluginMsg_DeviceEnumeration_NotifyDeviceChange( + callback_id2, data)))); + EXPECT_FALSE(helper2.called()); +} + +} // namespace proxy +} // namespace ppapi diff --git a/ppapi/proxy/plugin_dispatcher.cc b/ppapi/proxy/plugin_dispatcher.cc index 916970a..b864d99 100644 --- a/ppapi/proxy/plugin_dispatcher.cc +++ b/ppapi/proxy/plugin_dispatcher.cc @@ -343,8 +343,9 @@ void PluginDispatcher::LockedDispatchResourceReply( Resource* resource = PpapiGlobals::Get()->GetResourceTracker()->GetResource( reply_params.pp_resource()); if (!resource) { - if (reply_params.sequence()) - NOTREACHED(); + DLOG_IF(INFO, reply_params.sequence() != 0) + << "Pepper resource reply message received but the resource doesn't " + "exist (probably has been destroyed)."; return; } resource->OnReplyReceived(reply_params, nested_msg); diff --git a/ppapi/proxy/plugin_resource.h b/ppapi/proxy/plugin_resource.h index 0e6d64d..2e2e10f 100644 --- a/ppapi/proxy/plugin_resource.h +++ b/ppapi/proxy/plugin_resource.h @@ -26,6 +26,11 @@ class PluginDispatcher; class PPAPI_PROXY_EXPORT PluginResource : public Resource { public: + enum Destination { + RENDERER = 0, + BROWSER = 1 + }; + PluginResource(Connection connection, PP_Instance instance); virtual ~PluginResource(); @@ -48,16 +53,6 @@ class PPAPI_PROXY_EXPORT PluginResource : public Resource { virtual void NotifyLastPluginRefWasDeleted() OVERRIDE; virtual void NotifyInstanceWasDeleted() OVERRIDE; - protected: - enum Destination { - RENDERER = 0, - BROWSER = 1 - }; - - IPC::Sender* GetSender(Destination dest) { - return dest == RENDERER ? connection_.renderer_sender : - connection_.browser_sender; - } // Sends a create message to the browser or renderer for the current resource. void SendCreate(Destination dest, const IPC::Message& msg); @@ -137,6 +132,11 @@ class PPAPI_PROXY_EXPORT PluginResource : public Resource { ResourceMessageReplyParams* reply_params); private: + IPC::Sender* GetSender(Destination dest) { + return dest == RENDERER ? connection_.renderer_sender : + connection_.browser_sender; + } + // Helper function to send a |PpapiHostMsg_ResourceCall| to the given // destination with |nested_msg| and |call_params|. bool SendResourceCall(Destination dest, diff --git a/ppapi/proxy/ppapi_messages.h b/ppapi/proxy/ppapi_messages.h index 39c9bdb..b5e04de 100644 --- a/ppapi/proxy/ppapi_messages.h +++ b/ppapi/proxy/ppapi_messages.h @@ -1588,9 +1588,6 @@ IPC_MESSAGE_CONTROL4(PpapiPluginMsg_WebSocket_ClosedReply, // Audio input. IPC_MESSAGE_CONTROL0(PpapiHostMsg_AudioInput_Create) -IPC_MESSAGE_CONTROL0(PpapiHostMsg_AudioInput_EnumerateDevices) -IPC_MESSAGE_CONTROL1(PpapiPluginMsg_AudioInput_EnumerateDevicesReply, - std::vector<ppapi::DeviceRefData> /* devices */) IPC_MESSAGE_CONTROL3(PpapiHostMsg_AudioInput_Open, std::string /* device_id */, PP_AudioSampleRate /* sample_rate */, @@ -1608,6 +1605,17 @@ IPC_MESSAGE_CONTROL1(PpapiHostMsg_Flash_GetProxyForURL, std::string /* url */) IPC_MESSAGE_CONTROL1(PpapiPluginMsg_Flash_GetProxyForURLReply, std::string /* proxy */) +// Device enumeration messages used by audio input and video capture. +IPC_MESSAGE_CONTROL0(PpapiHostMsg_DeviceEnumeration_EnumerateDevices) +IPC_MESSAGE_CONTROL1(PpapiPluginMsg_DeviceEnumeration_EnumerateDevicesReply, + std::vector<ppapi::DeviceRefData> /* devices */) +IPC_MESSAGE_CONTROL1(PpapiHostMsg_DeviceEnumeration_MonitorDeviceChange, + uint32_t /* callback_id */) +IPC_MESSAGE_CONTROL0(PpapiHostMsg_DeviceEnumeration_StopMonitoringDeviceChange) +IPC_MESSAGE_CONTROL2(PpapiPluginMsg_DeviceEnumeration_NotifyDeviceChange, + uint32_t /* callback_id */, + std::vector<ppapi::DeviceRefData> /* devices */) + // Flash clipboard. IPC_MESSAGE_CONTROL0(PpapiHostMsg_FlashClipboard_Create) IPC_MESSAGE_CONTROL1(PpapiHostMsg_FlashClipboard_RegisterCustomFormat, diff --git a/ppapi/shared_impl/ppb_device_ref_shared.h b/ppapi/shared_impl/ppb_device_ref_shared.h index 895baa5..9ea367e 100644 --- a/ppapi/shared_impl/ppb_device_ref_shared.h +++ b/ppapi/shared_impl/ppb_device_ref_shared.h @@ -21,6 +21,12 @@ namespace ppapi { struct PPAPI_SHARED_EXPORT DeviceRefData { DeviceRefData(); + bool operator==(const DeviceRefData& other) const { + return type == other.type && + name == other.name && + id == other.id; + } + PP_DeviceType_Dev type; std::string name; std::string id; diff --git a/ppapi/thunk/interfaces_ppb_public_dev.h b/ppapi/thunk/interfaces_ppb_public_dev.h index fd8706e..08ddc59 100644 --- a/ppapi/thunk/interfaces_ppb_public_dev.h +++ b/ppapi/thunk/interfaces_ppb_public_dev.h @@ -39,6 +39,8 @@ UNPROXIED_API(PPB_Widget) PROXIED_IFACE(NoAPIName, PPB_AUDIO_INPUT_DEV_INTERFACE_0_2, PPB_AudioInput_Dev_0_2) +PROXIED_IFACE(NoAPIName, PPB_AUDIO_INPUT_DEV_INTERFACE_0_3, + PPB_AudioInput_Dev_0_3) 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 e820c08..2e7b818 100644 --- a/ppapi/thunk/ppb_audio_input_api.h +++ b/ppapi/thunk/ppb_audio_input_api.h @@ -20,8 +20,13 @@ class PPB_AudioInput_API { public: virtual ~PPB_AudioInput_API() {} - virtual int32_t EnumerateDevices(PP_Resource* devices, + virtual int32_t EnumerateDevices0_2( + PP_Resource* devices, + scoped_refptr<TrackedCallback> callback) = 0; + virtual int32_t EnumerateDevices(const PP_ArrayOutput& output, scoped_refptr<TrackedCallback> callback) = 0; + virtual int32_t MonitorDeviceChange(PP_MonitorDeviceChangeCallback callback, + void* user_data) = 0; virtual int32_t Open(const std::string& device_id, PP_Resource config, PPB_AudioInput_Callback audio_input_callback, diff --git a/ppapi/thunk/ppb_audio_input_thunk.cc b/ppapi/thunk/ppb_audio_input_thunk.cc index 94d978d..5fa512e 100644 --- a/ppapi/thunk/ppb_audio_input_thunk.cc +++ b/ppapi/thunk/ppb_audio_input_thunk.cc @@ -31,17 +31,37 @@ PP_Bool IsAudioInput(PP_Resource resource) { return PP_FromBool(enter.succeeded()); } +int32_t EnumerateDevices0_2(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()->EnumerateDevices0_2(devices, + enter.callback())); +} + int32_t EnumerateDevices(PP_Resource audio_input, - PP_Resource* devices, + PP_ArrayOutput output, PP_CompletionCallback callback) { EnterAudioInput enter(audio_input, callback, true); if (enter.failed()) return enter.retval(); - return enter.SetResult(enter.object()->EnumerateDevices(devices, + return enter.SetResult(enter.object()->EnumerateDevices(output, enter.callback())); } +int32_t MonitorDeviceChange(PP_Resource audio_input, + PP_MonitorDeviceChangeCallback callback, + void* user_data) { + EnterAudioInput enter(audio_input, true); + if (enter.failed()) + return enter.retval(); + return enter.object()->MonitorDeviceChange(callback, user_data); +} + int32_t Open(PP_Resource audio_input, PP_Resource device_ref, PP_Resource config, @@ -98,7 +118,19 @@ void Close(PP_Resource audio_input) { const PPB_AudioInput_Dev_0_2 g_ppb_audioinput_0_2_thunk = { &Create, &IsAudioInput, + &EnumerateDevices0_2, + &Open, + &GetCurrentConfig, + &StartCapture, + &StopCapture, + &Close +}; + +const PPB_AudioInput_Dev_0_3 g_ppb_audioinput_0_3_thunk = { + &Create, + &IsAudioInput, &EnumerateDevices, + &MonitorDeviceChange, &Open, &GetCurrentConfig, &StartCapture, @@ -112,5 +144,9 @@ const PPB_AudioInput_Dev_0_2* GetPPB_AudioInput_Dev_0_2_Thunk() { return &g_ppb_audioinput_0_2_thunk; } +const PPB_AudioInput_Dev_0_3* GetPPB_AudioInput_Dev_0_3_Thunk() { + return &g_ppb_audioinput_0_3_thunk; +} + } // namespace thunk } // namespace ppapi |