summaryrefslogtreecommitdiffstats
path: root/ppapi
diff options
context:
space:
mode:
authoryzshen@chromium.org <yzshen@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-12-05 02:44:18 +0000
committeryzshen@chromium.org <yzshen@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-12-05 02:44:18 +0000
commit4f01c76ed760e674cb86f2b0a17a779431e2aa71 (patch)
treea2d874fb3f21b65e07f0d75db567487f6bd4a38a /ppapi
parent4afef6e27ecbcf6430c5d37881eca04c6b1f8d78 (diff)
downloadchromium_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')
-rw-r--r--ppapi/api/dev/ppb_audio_input_dev.idl50
-rw-r--r--ppapi/api/dev/ppb_device_ref_dev.idl16
-rw-r--r--ppapi/c/dev/ppb_audio_input_dev.h69
-rw-r--r--ppapi/c/dev/ppb_device_ref_dev.h24
-rw-r--r--ppapi/cpp/dev/audio_input_dev.cc73
-rw-r--r--ppapi/cpp/dev/audio_input_dev.h3
-rw-r--r--ppapi/examples/audio_input/audio_input.cc107
-rw-r--r--ppapi/examples/audio_input/audio_input.html97
-rw-r--r--ppapi/native_client/src/untrusted/pnacl_irt_shim/pnacl_shim.c78
-rw-r--r--ppapi/ppapi_proxy.gypi3
-rw-r--r--ppapi/ppapi_tests.gypi1
-rw-r--r--ppapi/proxy/audio_input_resource.cc56
-rw-r--r--ppapi/proxy/audio_input_resource.h28
-rw-r--r--ppapi/proxy/device_enumeration_resource_helper.cc200
-rw-r--r--ppapi/proxy/device_enumeration_resource_helper.h82
-rw-r--r--ppapi/proxy/device_enumeration_resource_helper_unittest.cc386
-rw-r--r--ppapi/proxy/plugin_dispatcher.cc5
-rw-r--r--ppapi/proxy/plugin_resource.h20
-rw-r--r--ppapi/proxy/ppapi_messages.h14
-rw-r--r--ppapi/shared_impl/ppb_device_ref_shared.h6
-rw-r--r--ppapi/thunk/interfaces_ppb_public_dev.h2
-rw-r--r--ppapi/thunk/ppb_audio_input_api.h7
-rw-r--r--ppapi/thunk/ppb_audio_input_thunk.cc40
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, &params, &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, &params, &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, &params2, &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,
+ &params3, &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