blob: 5f700ec3cb7a9b8f41fbfc7322132ed07bf8fc5b [file] [log] [blame]
[email protected]77b55502012-11-08 22:20:201// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "ppapi/proxy/audio_input_resource.h"
6
7#include "base/bind.h"
8#include "base/logging.h"
9#include "ipc/ipc_platform_file.h"
10#include "media/audio/audio_parameters.h"
11#include "media/audio/shared_memory_util.h"
12#include "ppapi/c/pp_errors.h"
13#include "ppapi/proxy/ppapi_messages.h"
14#include "ppapi/proxy/resource_message_params.h"
[email protected]eb5960da2013-01-16 23:23:5315#include "ppapi/proxy/serialized_handle.h"
[email protected]77b55502012-11-08 22:20:2016#include "ppapi/shared_impl/ppapi_globals.h"
[email protected]77b55502012-11-08 22:20:2017#include "ppapi/shared_impl/resource_tracker.h"
18#include "ppapi/shared_impl/tracked_callback.h"
19#include "ppapi/thunk/enter.h"
20#include "ppapi/thunk/ppb_audio_config_api.h"
21
22namespace ppapi {
23namespace proxy {
24
25AudioInputResource::AudioInputResource(
26 Connection connection,
27 PP_Instance instance)
28 : PluginResource(connection, instance),
29 open_state_(BEFORE_OPEN),
30 capturing_(false),
31 shared_memory_size_(0),
32 audio_input_callback_(NULL),
33 user_data_(NULL),
[email protected]4f01c762012-12-05 02:44:1834 ALLOW_THIS_IN_INITIALIZER_LIST(enumeration_helper_(this)) {
[email protected]77b55502012-11-08 22:20:2035 SendCreate(RENDERER, PpapiHostMsg_AudioInput_Create());
36}
37
38AudioInputResource::~AudioInputResource() {
39 Close();
40}
41
42thunk::PPB_AudioInput_API* AudioInputResource::AsPPB_AudioInput_API() {
43 return this;
44}
45
[email protected]4f01c762012-12-05 02:44:1846void AudioInputResource::OnReplyReceived(
47 const ResourceMessageReplyParams& params,
48 const IPC::Message& msg) {
49 if (!enumeration_helper_.HandleReply(params, msg))
50 PluginResource::OnReplyReceived(params, msg);
51}
52
53int32_t AudioInputResource::EnumerateDevices0_2(
[email protected]77b55502012-11-08 22:20:2054 PP_Resource* devices,
55 scoped_refptr<TrackedCallback> callback) {
[email protected]4f01c762012-12-05 02:44:1856 return enumeration_helper_.EnumerateDevices0_2(devices, callback);
57}
[email protected]77b55502012-11-08 22:20:2058
[email protected]4f01c762012-12-05 02:44:1859int32_t AudioInputResource::EnumerateDevices(
60 const PP_ArrayOutput& output,
61 scoped_refptr<TrackedCallback> callback) {
62 return enumeration_helper_.EnumerateDevices(output, callback);
63}
64
65int32_t AudioInputResource::MonitorDeviceChange(
66 PP_MonitorDeviceChangeCallback callback,
67 void* user_data) {
68 return enumeration_helper_.MonitorDeviceChange(callback, user_data);
[email protected]77b55502012-11-08 22:20:2069}
70
[email protected]8917a672013-02-25 17:18:0471int32_t AudioInputResource::Open(PP_Resource device_ref,
[email protected]77b55502012-11-08 22:20:2072 PP_Resource config,
73 PPB_AudioInput_Callback audio_input_callback,
74 void* user_data,
75 scoped_refptr<TrackedCallback> callback) {
[email protected]8917a672013-02-25 17:18:0476 std::string device_id;
77 // |device_id| remains empty if |device_ref| is 0, which means the default
78 // device.
79 if (device_ref != 0) {
80 thunk::EnterResourceNoLock<thunk::PPB_DeviceRef_API> enter_device_ref(
81 device_ref, true);
82 if (enter_device_ref.failed())
83 return PP_ERROR_BADRESOURCE;
84 device_id = enter_device_ref.object()->GetDeviceRefData().id;
85 }
86
[email protected]77b55502012-11-08 22:20:2087 if (TrackedCallback::IsPending(open_callback_))
88 return PP_ERROR_INPROGRESS;
89 if (open_state_ != BEFORE_OPEN)
90 return PP_ERROR_FAILED;
91
92 if (!audio_input_callback)
93 return PP_ERROR_BADARGUMENT;
94 thunk::EnterResourceNoLock<thunk::PPB_AudioConfig_API> enter_config(config,
95 true);
96 if (enter_config.failed())
97 return PP_ERROR_BADARGUMENT;
98
99 config_ = config;
100 audio_input_callback_ = audio_input_callback;
101 user_data_ = user_data;
102 open_callback_ = callback;
103
104 PpapiHostMsg_AudioInput_Open msg(
105 device_id, enter_config.object()->GetSampleRate(),
106 enter_config.object()->GetSampleFrameCount());
107 Call<PpapiPluginMsg_AudioInput_OpenReply>(
108 RENDERER, msg,
109 base::Bind(&AudioInputResource::OnPluginMsgOpenReply,
110 base::Unretained(this)));
111 return PP_OK_COMPLETIONPENDING;
112}
113
114PP_Resource AudioInputResource::GetCurrentConfig() {
115 // AddRef for the caller.
116 if (config_.get())
117 PpapiGlobals::Get()->GetResourceTracker()->AddRefResource(config_);
118 return config_;
119}
120
121PP_Bool AudioInputResource::StartCapture() {
122 if (open_state_ == CLOSED || (open_state_ == BEFORE_OPEN &&
123 !TrackedCallback::IsPending(open_callback_))) {
124 return PP_FALSE;
125 }
126 if (capturing_)
127 return PP_TRUE;
128
129 capturing_ = true;
130 // Return directly if the audio input device hasn't been opened. Capturing
131 // will be started once the open operation is completed.
132 if (open_state_ == BEFORE_OPEN)
133 return PP_TRUE;
134
135 StartThread();
136
137 Post(RENDERER, PpapiHostMsg_AudioInput_StartOrStop(true));
138 return PP_TRUE;
139}
140
141PP_Bool AudioInputResource::StopCapture() {
142 if (open_state_ == CLOSED)
143 return PP_FALSE;
144 if (!capturing_)
145 return PP_TRUE;
146
147 // If the audio input device hasn't been opened, set |capturing_| to false and
148 // return directly.
149 if (open_state_ == BEFORE_OPEN) {
150 capturing_ = false;
151 return PP_TRUE;
152 }
153
154 Post(RENDERER, PpapiHostMsg_AudioInput_StartOrStop(false));
155
156 StopThread();
157 capturing_ = false;
158
159 return PP_TRUE;
160}
161
162void AudioInputResource::Close() {
163 if (open_state_ == CLOSED)
164 return;
165
166 open_state_ = CLOSED;
167 Post(RENDERER, PpapiHostMsg_AudioInput_Close());
168 StopThread();
169
170 if (TrackedCallback::IsPending(open_callback_))
171 open_callback_->PostAbort();
172}
173
[email protected]4f01c762012-12-05 02:44:18174void AudioInputResource::LastPluginRefWasDeleted() {
175 enumeration_helper_.LastPluginRefWasDeleted();
[email protected]77b55502012-11-08 22:20:20176}
177
178void AudioInputResource::OnPluginMsgOpenReply(
179 const ResourceMessageReplyParams& params) {
180 if (open_state_ == BEFORE_OPEN && params.result() == PP_OK) {
181 IPC::PlatformFileForTransit socket_handle_for_transit =
182 IPC::InvalidPlatformFileForTransit();
183 params.TakeSocketHandleAtIndex(0, &socket_handle_for_transit);
184 base::SyncSocket::Handle socket_handle =
185 IPC::PlatformFileForTransitToPlatformFile(socket_handle_for_transit);
186 CHECK(socket_handle != base::SyncSocket::kInvalidHandle);
187
188 SerializedHandle serialized_shared_memory_handle =
189 params.TakeHandleOfTypeAtIndex(1, SerializedHandle::SHARED_MEMORY);
190 CHECK(serialized_shared_memory_handle.IsHandleValid());
191
192 // See the comment in pepper_audio_input_host.cc about how we must call
193 // TotalSharedMemorySizeInBytes to get the actual size of the buffer. Here,
194 // we must call PacketSizeInBytes to get back the size of the audio buffer,
195 // excluding the bytes that audio uses for book-keeping.
196 size_t shared_memory_size = media::PacketSizeInBytes(
197 serialized_shared_memory_handle.size());
198
199 open_state_ = OPENED;
200 SetStreamInfo(serialized_shared_memory_handle.shmem(), shared_memory_size,
201 socket_handle);
202 } else {
203 capturing_ = false;
204 }
205
206 // The callback may have been aborted by Close().
207 if (TrackedCallback::IsPending(open_callback_))
208 open_callback_->Run(params.result());
209}
210
211void AudioInputResource::SetStreamInfo(
212 base::SharedMemoryHandle shared_memory_handle,
213 size_t shared_memory_size,
214 base::SyncSocket::Handle socket_handle) {
215 socket_.reset(new base::CancelableSyncSocket(socket_handle));
216 shared_memory_.reset(new base::SharedMemory(shared_memory_handle, false));
217 shared_memory_size_ = shared_memory_size;
218
219 if (!shared_memory_->Map(shared_memory_size_)) {
220 PpapiGlobals::Get()->LogWithSource(pp_instance(), PP_LOGLEVEL_WARNING, "",
221 "Failed to map shared memory for PPB_AudioInput_Shared.");
222 }
223
224 // There is a pending capture request before SetStreamInfo().
225 if (capturing_) {
226 // Set |capturing_| to false so that the state looks consistent to
227 // StartCapture(), which will reset it to true.
228 capturing_ = false;
229 StartCapture();
230 }
231}
232
233void AudioInputResource::StartThread() {
234 // Don't start the thread unless all our state is set up correctly.
235 if (!audio_input_callback_ || !socket_.get() || !capturing_ ||
236 !shared_memory_->memory()) {
237 return;
238 }
239 DCHECK(!audio_input_thread_.get());
240 audio_input_thread_.reset(new base::DelegateSimpleThread(
241 this, "plugin_audio_input_thread"));
242 audio_input_thread_->Start();
243}
244
245void AudioInputResource::StopThread() {
246 // Shut down the socket to escape any hanging |Receive|s.
247 if (socket_.get())
248 socket_->Shutdown();
249 if (audio_input_thread_.get()) {
250 audio_input_thread_->Join();
251 audio_input_thread_.reset();
252 }
253}
254
255void AudioInputResource::Run() {
256 // The shared memory represents AudioInputBufferParameters and the actual data
257 // buffer.
258 media::AudioInputBuffer* buffer =
259 static_cast<media::AudioInputBuffer*>(shared_memory_->memory());
260 uint32_t data_buffer_size =
261 shared_memory_size_ - sizeof(media::AudioInputBufferParameters);
262 int pending_data;
263
264 while (sizeof(pending_data) == socket_->Receive(&pending_data,
265 sizeof(pending_data)) &&
266 pending_data >= 0) {
267 // While closing the stream, we may receive buffers whose size is different
268 // from |data_buffer_size|.
269 CHECK_LE(buffer->params.size, data_buffer_size);
270 if (buffer->params.size > 0)
271 audio_input_callback_(&buffer->audio[0], buffer->params.size, user_data_);
272 }
273}
274
275} // namespace proxy
276} // namespace ppapi