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

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

Issue 2755613002: Support audio output device enumeration and selection in PPAPI (Closed)
Patch Set: Fix format, Rebase Created 3 years, 8 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
« no previous file with comments | « ppapi/proxy/audio_output_resource.h ('k') | ppapi/proxy/interface_list.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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, config, audio_output_callback, user_data,
76 callback);
77 }
78
79 PP_Resource AudioOutputResource::GetCurrentConfig() {
80 // AddRef for the caller.
81 if (config_.get())
82 PpapiGlobals::Get()->GetResourceTracker()->AddRefResource(config_);
83 return config_;
84 }
85
86 PP_Bool AudioOutputResource::StartPlayback() {
87 if (open_state_ == CLOSED || (open_state_ == BEFORE_OPEN &&
88 !TrackedCallback::IsPending(open_callback_))) {
89 return PP_FALSE;
90 }
91 if (playing_)
92 return PP_TRUE;
93
94 playing_ = true;
95
96 StartThread();
97
98 Post(RENDERER, PpapiHostMsg_AudioOutput_StartOrStop(true));
99 return PP_TRUE;
100 }
101
102 PP_Bool AudioOutputResource::StopPlayback() {
103 if (open_state_ == CLOSED)
104 return PP_FALSE;
105 if (!playing_)
106 return PP_TRUE;
107
108 // If the audio output device hasn't been opened, set |playing_| to false and
109 // return directly.
110 if (open_state_ == BEFORE_OPEN) {
111 playing_ = false;
112 return PP_TRUE;
113 }
114
115 Post(RENDERER, PpapiHostMsg_AudioOutput_StartOrStop(false));
116
117 StopThread();
118 playing_ = false;
119
120 return PP_TRUE;
121 }
122
123 void AudioOutputResource::Close() {
124 if (open_state_ == CLOSED)
125 return;
126
127 open_state_ = CLOSED;
128 Post(RENDERER, PpapiHostMsg_AudioOutput_Close());
129 StopThread();
130
131 if (TrackedCallback::IsPending(open_callback_))
132 open_callback_->PostAbort();
133 }
134
135 void AudioOutputResource::LastPluginRefWasDeleted() {
136 enumeration_helper_.LastPluginRefWasDeleted();
137 }
138
139 void AudioOutputResource::OnPluginMsgOpenReply(
140 const ResourceMessageReplyParams& params) {
141 if (open_state_ == BEFORE_OPEN && params.result() == PP_OK) {
142 IPC::PlatformFileForTransit socket_handle_for_transit =
143 IPC::InvalidPlatformFileForTransit();
144 params.TakeSocketHandleAtIndex(0, &socket_handle_for_transit);
145 base::SyncSocket::Handle socket_handle =
146 IPC::PlatformFileForTransitToPlatformFile(socket_handle_for_transit);
147 CHECK(socket_handle != base::SyncSocket::kInvalidHandle);
148
149 SerializedHandle serialized_shared_memory_handle =
150 params.TakeHandleOfTypeAtIndex(1, SerializedHandle::SHARED_MEMORY);
151 CHECK(serialized_shared_memory_handle.IsHandleValid());
152
153 open_state_ = OPENED;
154 SetStreamInfo(serialized_shared_memory_handle.shmem(),
155 serialized_shared_memory_handle.size(), socket_handle);
156 } else {
157 playing_ = false;
158 }
159
160 // The callback may have been aborted by Close().
161 if (TrackedCallback::IsPending(open_callback_))
162 open_callback_->Run(params.result());
163 }
164
165 void AudioOutputResource::SetStreamInfo(
166 base::SharedMemoryHandle shared_memory_handle,
167 size_t shared_memory_size,
168 base::SyncSocket::Handle socket_handle) {
169 socket_.reset(new base::CancelableSyncSocket(socket_handle));
170 shared_memory_.reset(new base::SharedMemory(shared_memory_handle, false));
171 shared_memory_size_ = shared_memory_size;
172 DCHECK(!shared_memory_->memory());
173
174 // If we fail to map the shared memory into the caller's address space we
175 // might as well fail here since nothing will work if this is the case.
176 CHECK(shared_memory_->Map(shared_memory_size_));
177
178 // Create a new audio bus and wrap the audio data section in shared memory.
179 media::AudioOutputBuffer* buffer =
180 static_cast<media::AudioOutputBuffer*>(shared_memory_->memory());
181 audio_bus_ = media::AudioBus::WrapMemory(kAudioOutputChannels,
182 sample_frame_count_, buffer->audio);
183
184 // Ensure that the size of the created audio bus matches the allocated
185 // size in shared memory.
186 // Example: DCHECK_EQ(8208 - 16, 8192) for |sample_frame_count_| = 2048.
187 const uint32_t audio_bus_size_bytes = media::AudioBus::CalculateMemorySize(
188 audio_bus_->channels(), audio_bus_->frames());
189 DCHECK_EQ(shared_memory_size_ - sizeof(media::AudioOutputBufferParameters),
190 audio_bus_size_bytes);
191
192 // Setup integer audio buffer for user audio data
193 client_buffer_size_bytes_ = audio_bus_->frames() * audio_bus_->channels() *
194 kBitsPerAudioOutputSample / 8;
195 client_buffer_.reset(new uint8_t[client_buffer_size_bytes_]);
196 }
197
198 void AudioOutputResource::StartThread() {
199 // Don't start the thread unless all our state is set up correctly.
200 if (!audio_output_callback_ || !socket_.get() || !shared_memory_->memory() ||
201 !audio_bus_.get() || !client_buffer_.get() || bytes_per_second_ == 0)
202 return;
203
204 // Clear contents of shm buffer before starting audio thread. This will
205 // prevent a burst of static if for some reason the audio thread doesn't
206 // start up quickly enough.
207 memset(shared_memory_->memory(), 0, shared_memory_size_);
208 memset(client_buffer_.get(), 0, client_buffer_size_bytes_);
209
210 DCHECK(!audio_output_thread_.get());
211 audio_output_thread_.reset(
212 new base::DelegateSimpleThread(this, "plugin_audio_output_thread"));
213 audio_output_thread_->Start();
214 }
215
216 void AudioOutputResource::StopThread() {
217 // Shut down the socket to escape any hanging |Receive|s.
218 if (socket_.get())
219 socket_->Shutdown();
220 if (audio_output_thread_.get()) {
221 audio_output_thread_->Join();
222 audio_output_thread_.reset();
223 }
224 }
225
226 void AudioOutputResource::Run() {
227 // The shared memory represents AudioOutputBufferParameters and the actual
228 // data buffer stored as an audio bus.
229 media::AudioOutputBuffer* buffer =
230 static_cast<media::AudioOutputBuffer*>(shared_memory_->memory());
231
232 // This is a constantly increasing counter that is used to verify on the
233 // browser side that buffers are in sync.
234 uint32_t buffer_index = 0;
235
236 while (true) {
237 int pending_data = 0;
238 size_t bytes_read = socket_->Receive(&pending_data, sizeof(pending_data));
239 if (bytes_read != sizeof(pending_data)) {
240 DCHECK_EQ(bytes_read, 0U);
241 break;
242 }
243 if (pending_data < 0)
244 break;
245
246 {
247 base::TimeDelta delay =
248 base::TimeDelta::FromMicroseconds(buffer->params.delay);
249
250 audio_output_callback_(client_buffer_.get(), client_buffer_size_bytes_,
251 delay.InSecondsF(), user_data_);
252 }
253
254 // Deinterleave the audio data into the shared memory as floats.
255 audio_bus_->FromInterleaved(client_buffer_.get(), audio_bus_->frames(),
256 kBitsPerAudioOutputSample / 8);
257
258 // Inform other side that we have read the data from the shared memory.
259 // Let the other end know which buffer we just filled. The buffer index is
260 // used to ensure the other end is getting the buffer it expects. For more
261 // details on how this works see AudioSyncReader::WaitUntilDataIsReady().
262 ++buffer_index;
263 size_t bytes_sent = socket_->Send(&buffer_index, sizeof(buffer_index));
264 if (bytes_sent != sizeof(buffer_index)) {
265 DCHECK_EQ(bytes_sent, 0U);
266 break;
267 }
268 }
269 }
270
271 int32_t AudioOutputResource::CommonOpen(
272 PP_Resource device_ref,
273 PP_Resource config,
274 PPB_AudioOutput_Callback audio_output_callback,
275 void* user_data,
276 scoped_refptr<TrackedCallback> callback) {
277 std::string device_id;
278 // |device_id| remains empty if |device_ref| is 0, which means the default
279 // device.
280 if (device_ref != 0) {
281 thunk::EnterResourceNoLock<thunk::PPB_DeviceRef_API> enter_device_ref(
282 device_ref, true);
283 if (enter_device_ref.failed())
284 return PP_ERROR_BADRESOURCE;
285 device_id = enter_device_ref.object()->GetDeviceRefData().id;
286 }
287
288 if (TrackedCallback::IsPending(open_callback_))
289 return PP_ERROR_INPROGRESS;
290 if (open_state_ != BEFORE_OPEN)
291 return PP_ERROR_FAILED;
292
293 if (!audio_output_callback)
294 return PP_ERROR_BADARGUMENT;
295 thunk::EnterResourceNoLock<thunk::PPB_AudioConfig_API> enter_config(config,
296 true);
297 if (enter_config.failed())
298 return PP_ERROR_BADARGUMENT;
299
300 config_ = config;
301 audio_output_callback_ = audio_output_callback;
302 user_data_ = user_data;
303 open_callback_ = callback;
304 bytes_per_second_ = kAudioOutputChannels * (kBitsPerAudioOutputSample / 8) *
305 enter_config.object()->GetSampleRate();
306 sample_frame_count_ = enter_config.object()->GetSampleFrameCount();
307
308 PpapiHostMsg_AudioOutput_Open msg(
309 device_id, enter_config.object()->GetSampleRate(),
310 enter_config.object()->GetSampleFrameCount());
311 Call<PpapiPluginMsg_AudioOutput_OpenReply>(
312 RENDERER, msg,
313 base::Bind(&AudioOutputResource::OnPluginMsgOpenReply,
314 base::Unretained(this)));
315 return PP_OK_COMPLETIONPENDING;
316 }
317 } // namespace proxy
318 } // namespace ppapi
OLDNEW
« no previous file with comments | « ppapi/proxy/audio_output_resource.h ('k') | ppapi/proxy/interface_list.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698