Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1676)

Side by Side Diff: ppapi/proxy/audio_output_resource.cc

Issue 2755613002: Support audio output device enumeration and selection in PPAPI (Closed)
Patch Set: Should not include local change to ppapi/generators/idl_outfile.py Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2017 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_output_resource.h"
6
7 #include "base/bind.h"
8 #include "base/logging.h"
9 #include "base/numerics/safe_conversions.h"
10 #include "ipc/ipc_platform_file.h"
11 #include "media/base/audio_bus.h"
12 #include "media/base/audio_parameters.h"
13 #include "ppapi/c/pp_errors.h"
14 #include "ppapi/proxy/ppapi_messages.h"
15 #include "ppapi/proxy/resource_message_params.h"
16 #include "ppapi/proxy/serialized_handle.h"
17 #include "ppapi/shared_impl/ppapi_globals.h"
18 #include "ppapi/shared_impl/ppb_audio_config_shared.h"
19 #include "ppapi/shared_impl/resource_tracker.h"
20 #include "ppapi/shared_impl/tracked_callback.h"
21 #include "ppapi/thunk/enter.h"
22 #include "ppapi/thunk/ppb_audio_config_api.h"
23
24 namespace ppapi {
25 namespace proxy {
26
27 AudioOutputResource::AudioOutputResource(Connection connection,
28 PP_Instance instance)
29 : PluginResource(connection, instance),
30 open_state_(BEFORE_OPEN),
31 playing_(false),
32 shared_memory_size_(0),
33 audio_output_callback_(NULL),
34 user_data_(NULL),
35 enumeration_helper_(this),
36 bytes_per_second_(0),
37 sample_frame_count_(0),
38 client_buffer_size_bytes_(0) {
39 SendCreate(RENDERER, PpapiHostMsg_AudioOutput_Create());
40 }
41
42 AudioOutputResource::~AudioOutputResource() {
43 Close();
44 }
45
46 thunk::PPB_AudioOutput_API* AudioOutputResource::AsPPB_AudioOutput_API() {
47 return this;
48 }
49
50 void AudioOutputResource::OnReplyReceived(
51 const ResourceMessageReplyParams& params,
52 const IPC::Message& msg) {
53 if (!enumeration_helper_.HandleReply(params, msg))
54 PluginResource::OnReplyReceived(params, msg);
55 }
56
57 int32_t AudioOutputResource::EnumerateDevices(
58 const PP_ArrayOutput& output,
59 scoped_refptr<TrackedCallback> callback) {
60 return enumeration_helper_.EnumerateDevices(output, callback);
61 }
62
63 int32_t AudioOutputResource::MonitorDeviceChange(
64 PP_MonitorDeviceChangeCallback callback,
65 void* user_data) {
66 return enumeration_helper_.MonitorDeviceChange(callback, user_data);
67 }
68
69 int32_t AudioOutputResource::Open(
70 PP_Resource device_ref,
71 PP_Resource config,
72 PPB_AudioOutput_Callback audio_output_callback,
73 void* user_data,
74 scoped_refptr<TrackedCallback> callback) {
75 return CommonOpen(device_ref,
76 config,
77 audio_output_callback,
78 user_data,
79 callback);
80 }
81
82 PP_Resource AudioOutputResource::GetCurrentConfig() {
83 // AddRef for the caller.
84 if (config_.get())
85 PpapiGlobals::Get()->GetResourceTracker()->AddRefResource(config_);
86 return config_;
87 }
88
89 PP_Bool AudioOutputResource::StartPlayback() {
90 if (open_state_ == CLOSED || (open_state_ == BEFORE_OPEN &&
91 !TrackedCallback::IsPending(open_callback_))) {
92 return PP_FALSE;
93 }
94 if (playing_)
95 return PP_TRUE;
96
97 playing_ = true;
98
99 StartThread();
100
101 Post(RENDERER, PpapiHostMsg_AudioOutput_StartOrStop(true));
102 return PP_TRUE;
103 }
104
105 PP_Bool AudioOutputResource::StopPlayback() {
106 if (open_state_ == CLOSED)
107 return PP_FALSE;
108 if (!playing_)
109 return PP_TRUE;
110
111 // If the audio output device hasn't been opened, set |playing_| to false and
112 // return directly.
113 if (open_state_ == BEFORE_OPEN) {
114 playing_ = false;
115 return PP_TRUE;
116 }
117
118 Post(RENDERER, PpapiHostMsg_AudioOutput_StartOrStop(false));
119
120 StopThread();
121 playing_ = false;
122
123 return PP_TRUE;
124 }
125
126 void AudioOutputResource::Close() {
127 if (open_state_ == CLOSED)
128 return;
129
130 open_state_ = CLOSED;
131 Post(RENDERER, PpapiHostMsg_AudioOutput_Close());
132 StopThread();
133
134 if (TrackedCallback::IsPending(open_callback_))
135 open_callback_->PostAbort();
136 }
137
138 void AudioOutputResource::LastPluginRefWasDeleted() {
139 enumeration_helper_.LastPluginRefWasDeleted();
140 }
141
142 void AudioOutputResource::OnPluginMsgOpenReply(
143 const ResourceMessageReplyParams& params) {
144 if (open_state_ == BEFORE_OPEN && params.result() == PP_OK) {
145 IPC::PlatformFileForTransit socket_handle_for_transit =
146 IPC::InvalidPlatformFileForTransit();
147 params.TakeSocketHandleAtIndex(0, &socket_handle_for_transit);
148 base::SyncSocket::Handle socket_handle =
149 IPC::PlatformFileForTransitToPlatformFile(socket_handle_for_transit);
150 CHECK(socket_handle != base::SyncSocket::kInvalidHandle);
151
152 SerializedHandle serialized_shared_memory_handle =
153 params.TakeHandleOfTypeAtIndex(1, SerializedHandle::SHARED_MEMORY);
154 CHECK(serialized_shared_memory_handle.IsHandleValid());
155
156 open_state_ = OPENED;
157 SetStreamInfo(serialized_shared_memory_handle.shmem(),
158 serialized_shared_memory_handle.size(),
159 socket_handle);
160 } else {
161 playing_ = false;
162 }
163
164 // The callback may have been aborted by Close().
165 if (TrackedCallback::IsPending(open_callback_))
166 open_callback_->Run(params.result());
167 }
168
169 void AudioOutputResource::SetStreamInfo(
170 base::SharedMemoryHandle shared_memory_handle,
171 size_t shared_memory_size,
172 base::SyncSocket::Handle socket_handle) {
173 socket_.reset(new base::CancelableSyncSocket(socket_handle));
174 shared_memory_.reset(new base::SharedMemory(shared_memory_handle, false));
175 shared_memory_size_ = shared_memory_size;
176 DCHECK(!shared_memory_->memory());
177
178 // If we fail to map the shared memory into the caller's address space we
179 // might as well fail here since nothing will work if this is the case.
180 CHECK(shared_memory_->Map(shared_memory_size_));
181
182 // Create a new audio bus and wrap the audio data section in shared memory.
183 media::AudioOutputBuffer* buffer =
184 static_cast<media::AudioOutputBuffer*>(shared_memory_->memory());
185 audio_bus_ = media::AudioBus::WrapMemory(
186 kAudioOutputChannels, sample_frame_count_, buffer->audio);
187
188 // Ensure that the size of the created audio bus matches the allocated
189 // size in shared memory.
190 // Example: DCHECK_EQ(8208 - 16, 8192) for |sample_frame_count_| = 2048.
191 const uint32_t audio_bus_size_bytes = media::AudioBus::CalculateMemorySize(
192 audio_bus_->channels(), audio_bus_->frames());
193 DCHECK_EQ(shared_memory_size_ - sizeof(media::AudioOutputBufferParameters),
194 audio_bus_size_bytes);
195
196 // Setup integer audio buffer for user audio data
197 client_buffer_size_bytes_ = audio_bus_->frames() * audio_bus_->channels() *
198 kBitsPerAudioOutputSample / 8;
199 client_buffer_.reset(new uint8_t[client_buffer_size_bytes_]);
200 }
201
202 void AudioOutputResource::StartThread() {
203 // Don't start the thread unless all our state is set up correctly.
204 if (!audio_output_callback_ || !socket_.get() ||
205 !shared_memory_->memory() || !audio_bus_.get() || !client_buffer_.get() ||
206 bytes_per_second_ == 0)
207 return;
208
209 // Clear contents of shm buffer before starting audio thread. This will
210 // prevent a burst of static if for some reason the audio thread doesn't
211 // start up quickly enough.
212 memset(shared_memory_->memory(), 0, shared_memory_size_);
213 memset(client_buffer_.get(), 0, client_buffer_size_bytes_);
214
215 DCHECK(!audio_output_thread_.get());
216 audio_output_thread_.reset(new base::DelegateSimpleThread(
217 this, "plugin_audio_output_thread"));
218 audio_output_thread_->Start();
219 }
220
221 void AudioOutputResource::StopThread() {
222 // Shut down the socket to escape any hanging |Receive|s.
223 if (socket_.get())
224 socket_->Shutdown();
225 if (audio_output_thread_.get()) {
226 audio_output_thread_->Join();
227 audio_output_thread_.reset();
228 }
229 }
230
231 void AudioOutputResource::Run() {
232 // The shared memory represents AudioOutputBufferParameters and the actual
233 // data buffer stored as an audio bus.
234 media::AudioOutputBuffer* buffer =
235 static_cast<media::AudioOutputBuffer*>(shared_memory_->memory());
236
237 // This is a constantly increasing counter that is used to verify on the
238 // browser side that buffers are in sync.
239 uint32_t buffer_index = 0;
240
241 while (true) {
242 int pending_data = 0;
243 size_t bytes_read = socket_->Receive(&pending_data, sizeof(pending_data));
244 if (bytes_read != sizeof(pending_data)) {
245 DCHECK_EQ(bytes_read, 0U);
246 break;
247 }
248 if (pending_data < 0)
249 break;
250
251 {
252 base::TimeDelta delay =
253 base::TimeDelta::FromMicroseconds(buffer->params.delay);
254
255 audio_output_callback_(client_buffer_.get(),
256 client_buffer_size_bytes_,
257 delay.InSecondsF(),
258 user_data_);
259 }
260
261 // Deinterleave the audio data into the shared memory as floats.
262 audio_bus_->FromInterleaved(client_buffer_.get(),
263 audio_bus_->frames(),
264 kBitsPerAudioOutputSample / 8);
265
266 // Inform other side that we have read the data from the shared memory.
267 // Let the other end know which buffer we just filled. The buffer index is
268 // used to ensure the other end is getting the buffer it expects. For more
269 // details on how this works see AudioSyncReader::WaitUntilDataIsReady().
270 ++buffer_index;
271 size_t bytes_sent = socket_->Send(&buffer_index, sizeof(buffer_index));
272 if (bytes_sent != sizeof(buffer_index)) {
273 DCHECK_EQ(bytes_sent, 0U);
274 break;
275 }
276 }
277 }
278
279 int32_t AudioOutputResource::CommonOpen(
280 PP_Resource device_ref,
281 PP_Resource config,
282 PPB_AudioOutput_Callback audio_output_callback,
283 void* user_data,
284 scoped_refptr<TrackedCallback> callback) {
285 std::string device_id;
286 // |device_id| remains empty if |device_ref| is 0, which means the default
287 // device.
288 if (device_ref != 0) {
289 thunk::EnterResourceNoLock<thunk::PPB_DeviceRef_API> enter_device_ref(
290 device_ref, true);
291 if (enter_device_ref.failed())
292 return PP_ERROR_BADRESOURCE;
293 device_id = enter_device_ref.object()->GetDeviceRefData().id;
294 }
295
296 if (TrackedCallback::IsPending(open_callback_))
297 return PP_ERROR_INPROGRESS;
298 if (open_state_ != BEFORE_OPEN)
299 return PP_ERROR_FAILED;
300
301 if (!audio_output_callback)
302 return PP_ERROR_BADARGUMENT;
303 thunk::EnterResourceNoLock<thunk::PPB_AudioConfig_API> enter_config(config,
304 true);
305 if (enter_config.failed())
306 return PP_ERROR_BADARGUMENT;
307
308 config_ = config;
309 audio_output_callback_ = audio_output_callback;
310 user_data_ = user_data;
311 open_callback_ = callback;
312 bytes_per_second_ = kAudioOutputChannels * (kBitsPerAudioOutputSample / 8) *
313 enter_config.object()->GetSampleRate();
314 sample_frame_count_ = enter_config.object()->GetSampleFrameCount();
315
316 PpapiHostMsg_AudioOutput_Open msg(
317 device_id, enter_config.object()->GetSampleRate(),
318 enter_config.object()->GetSampleFrameCount());
319 Call<PpapiPluginMsg_AudioOutput_OpenReply>(
320 RENDERER, msg,
321 base::Bind(&AudioOutputResource::OnPluginMsgOpenReply,
322 base::Unretained(this)));
323 return PP_OK_COMPLETIONPENDING;
324 }
325 } // namespace proxy
326 } // namespace ppapi
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698