1 : /*
2 : * Copyright (c) 2011 The Native Client Authors. All rights reserved.
3 : * Use of this source code is governed by a BSD-style license that can be
4 : * found in the LICENSE file.
5 : */
6 :
7 : // This file provides limited support for pepper audio emulation
8 : // NOTE: Though, it looks like that multiple audio streams are
9 : // supported, this is not the case at this point.
10 :
11 :
12 : #include <string.h>
13 : #include <string>
14 :
15 : #if (NACL_LINUX)
16 : // for shmem
17 : #include "native_client/src/trusted/desc/linux/nacl_desc_sysv_shm.h"
18 : // for sync_sockets
19 : #include <sys/socket.h>
20 : #endif
21 :
22 : #include "ppapi/c/pp_errors.h"
23 : #include "ppapi/c/pp_size.h"
24 : #include "ppapi/c/ppb_audio.h"
25 : #include "ppapi/c/ppb_audio_config.h"
26 :
27 : #include "native_client/src/shared/imc/nacl_imc.h"
28 : #include "native_client/src/shared/platform/nacl_check.h"
29 : #include "native_client/src/shared/srpc/nacl_srpc.h"
30 : #include "native_client/src/trusted/desc/nacl_desc_sync_socket.h"
31 : #include "native_client/src/trusted/desc/nacl_desc_wrapper.h"
32 :
33 : #include "native_client/src/trusted/sel_universal/rpc_universal.h"
34 : #include "native_client/src/trusted/sel_universal/parsing.h"
35 : #include "native_client/src/trusted/sel_universal/primitives.h"
36 : #include "native_client/src/trusted/sel_universal/pepper_emu.h"
37 : #include "native_client/src/trusted/sel_universal/pepper_emu_helper.h"
38 : #include "native_client/src/trusted/sel_universal/srpc_helper.h"
39 :
40 : using nacl::DescWrapperFactory;
41 : using nacl::DescWrapper;
42 :
43 : // ======================================================================
44 : namespace {
45 :
46 : const int kBytesPerSample = 4; // 16-bit stereo
47 :
48 : const int kRecommendSampleFrameCount = 4 * 1024;
49 : const int kMaxAudioBufferSize = 64 * 1024;
50 :
51 : IMultimedia* GlobalMultiMediaInterface = 0;
52 :
53 : // ======================================================================
54 : // Note: Just a bunch of fairly unrelated global variables,
55 : // we expect them to be zero initialized.
56 : struct DataAudio {
57 : int handle_config;
58 :
59 : nacl::DescWrapper* desc_shmem;
60 : nacl::DescWrapper* desc_sync_in;
61 : nacl::DescWrapper* desc_sync_out;
62 : void* addr_audio;
63 : };
64 :
65 :
66 : struct DataAudioConfig {
67 : int sample_frequency;
68 : int sample_frame_count;
69 : };
70 :
71 : // For now we handle only one resource each which is sufficient for SDL
72 22 : Resource<DataAudio> GlobalAudioDataResources(1, "audio");
73 22 : Resource<DataAudioConfig> GlobalAudioConfigDataResources(1, "audio_config");
74 :
75 : // ======================================================================
76 0 : void AudioCallBack(void* user_data, unsigned char* buffer, int length) {
77 0 : NaClLog(2, "AudioCallBack(%p, %p, %d)\n", user_data, buffer, length);
78 0 : DataAudio* data = reinterpret_cast<DataAudio*>(user_data);
79 : // NOTE: we copy the previously filled buffer.
80 : // This introduces extra latency but simplifies the design
81 : // as we do not have to wait for the nexe to generate the data.
82 0 : memcpy(buffer, data->addr_audio, length);
83 0 : int value = 0;
84 0 : data->desc_sync_in->Write(&value, sizeof value);
85 0 : }
86 :
87 : // From the PPB_Audio API
88 : // PP_Resource Create(PP_Instance instance, PP_Resource config,
89 : // PPB_Audio_Callback audio_callback, void* user_data);
90 : // PPB_Audio_Create:ii:i
91 0 : void PPB_Audio_Create(SRPC_PARAMS) {
92 0 : int instance = ins[0]->u.ival;
93 0 : int config_handle = ins[1]->u.ival;
94 0 : NaClLog(1, "PPB_Audio_Create(%d, %d)\n", instance, config_handle);
95 :
96 0 : const int handle = GlobalAudioDataResources.Alloc();
97 : // for now only support one audio resource
98 0 : DataAudio* data = GlobalAudioDataResources.GetDataForHandle(handle);
99 0 : data->handle_config = config_handle;
100 : DataAudioConfig* config_data =
101 0 : GlobalAudioConfigDataResources.GetDataForHandle(config_handle);
102 :
103 0 : nacl::DescWrapperFactory factory;
104 : #if (NACL_WINDOWS || NACL_OSX)
105 0 : NaClLog(LOG_ERROR, "HandlerSyncSocketCreate NYI for windows/mac\n");
106 : #else
107 : nacl::Handle handles[2] = {nacl::kInvalidHandle, nacl::kInvalidHandle};
108 : if (socketpair(AF_UNIX, SOCK_STREAM, 0, handles) != 0) {
109 : NaClLog(LOG_FATAL, "cannot create syn sockets\n");
110 : }
111 : data->desc_sync_in = factory.ImportSyncSocketHandle(handles[0]);
112 : data->desc_sync_out = factory.ImportSyncSocketHandle(handles[1]);
113 : #endif
114 0 : data->desc_shmem = factory.MakeShm(kMaxAudioBufferSize);
115 : size_t dummy_size;
116 :
117 0 : if (data->desc_shmem->Map(&data->addr_audio, &dummy_size)) {
118 0 : NaClLog(LOG_FATAL, "cannot map audio shmem\n");
119 : }
120 0 : NaClLog(LOG_INFO, "PPB_Audio_Create: buffer is %p\n", data->addr_audio);
121 :
122 : GlobalMultiMediaInterface->AudioInit16Bit(config_data->sample_frequency,
123 : 2,
124 : config_data->sample_frame_count,
125 : &AudioCallBack,
126 0 : data);
127 0 : outs[0]->u.ival = handle;
128 0 : NaClLog(1, "PPB_Audio_Create -> %d\n", handle);
129 0 : rpc->result = NACL_SRPC_RESULT_OK;
130 0 : done->Run(done);
131 :
132 : UserEvent* event =
133 0 : MakeUserEvent(EVENT_TYPE_INIT_AUDIO, 0, handle, data, 0);
134 :
135 0 : GlobalMultiMediaInterface->PushUserEvent(event);
136 0 : }
137 :
138 : // From the PPB_Audio API
139 : // PP_Bool IsAudio(PP_Resource resource)
140 : // PPB_Audio_IsAudio:i:i
141 0 : void PPB_Audio_IsAudio(SRPC_PARAMS) {
142 0 : int handle = ins[0]->u.ival;
143 0 : NaClLog(1, "PPB_Audio_IsAudio(%d)\n", handle);
144 0 : DataAudio* data = GlobalAudioDataResources.GetDataForHandle(handle);
145 : // for now be strict to catch problems early
146 0 : CHECK(data != NULL);
147 0 : outs[0]->u.ival = 1;
148 0 : rpc->result = NACL_SRPC_RESULT_OK;
149 0 : done->Run(done);
150 0 : }
151 :
152 : // From the PPB_Audio API
153 : // PP_Resource GetCurrentConfig(PP_Resource audio);
154 : // PPB_Audio_GetCurrentConfig:i:i
155 0 : void PPB_Audio_GetCurrentConfig(SRPC_PARAMS) {
156 0 : int handle = ins[0]->u.ival;
157 0 : NaClLog(1, "PPB_Audio_GetCurrentConfig(%d)\n", handle);
158 0 : DataAudio* data = GlobalAudioDataResources.GetDataForHandle(handle);
159 0 : outs[0]->u.ival = data->handle_config;
160 0 : rpc->result = NACL_SRPC_RESULT_OK;
161 0 : done->Run(done);
162 0 : }
163 :
164 : // From the PPB_Audio API
165 : // PP_Bool StartPlayback(PP_Resource audio);
166 : // PPB_Audio_StopPlayback:i:i
167 0 : void PPB_Audio_StopPlayback(SRPC_PARAMS) {
168 0 : int handle = ins[0]->u.ival;
169 0 : NaClLog(1, "PPB_Audio_StopPlayback(%d)\n", handle);
170 0 : DataAudio* data = GlobalAudioDataResources.GetDataForHandle(handle);
171 0 : CHECK(data != NULL);
172 0 : GlobalMultiMediaInterface->AudioStop();
173 0 : outs[0]->u.ival = 1;
174 0 : rpc->result = NACL_SRPC_RESULT_OK;
175 0 : done->Run(done);
176 0 : }
177 :
178 : // From the PPB_Audio API
179 : // PP_Bool StopPlayback(PP_Resource audio);
180 : // PPB_Audio_StartPlayback:i:i
181 0 : void PPB_Audio_StartPlayback(SRPC_PARAMS) {
182 0 : int handle = ins[0]->u.ival;
183 0 : NaClLog(1, "PPB_Audio_StartPlayback(%d)\n", handle);
184 0 : DataAudio* data = GlobalAudioDataResources.GetDataForHandle(handle);
185 0 : CHECK(data != NULL);
186 0 : GlobalMultiMediaInterface->AudioStart();
187 :
188 0 : outs[0]->u.ival = 1;
189 0 : rpc->result = NACL_SRPC_RESULT_OK;
190 0 : done->Run(done);
191 0 : }
192 :
193 : // From the PPB_AudioConfig API
194 : // PP_Resource CreateStereo16Bit(PP_Instance instance,
195 : // PP_AudioSampleRate sample_rate,
196 : // uint32_t sample_frame_count);
197 : // PPB_AudioConfig_CreateStereo16Bit:iii:i
198 0 : void PPB_AudioConfig_CreateStereo16Bit(SRPC_PARAMS) {
199 0 : int instance = ins[0]->u.ival;
200 0 : int sample_frequency = ins[1]->u.ival;
201 0 : int sample_frame_count = ins[2]->u.ival;
202 : NaClLog(1, "PPB_AudioConfig_CreateStereo16Bit(%d, %d, %d)\n",
203 0 : instance, sample_frequency, sample_frame_count);
204 :
205 0 : const int handle = GlobalAudioConfigDataResources.Alloc();
206 : DataAudioConfig* data =
207 0 : GlobalAudioConfigDataResources.GetDataForHandle(handle);
208 0 : data->sample_frequency = sample_frequency;
209 0 : data->sample_frame_count = sample_frame_count;
210 :
211 0 : CHECK(sample_frame_count * kBytesPerSample < kMaxAudioBufferSize);
212 0 : outs[0]->u.ival = handle;
213 0 : NaClLog(1, "PPB_AudioConfig_CreateStereo16Bit -> %d\n", handle);
214 0 : rpc->result = NACL_SRPC_RESULT_OK;
215 0 : done->Run(done);
216 0 : }
217 :
218 : // PPB_AudioConfig_IsAudioConfig:i:i
219 : // PP_Bool IsAudioConfig(PP_Resource resource);
220 0 : void PPB_AudioConfig_IsAudioConfig(SRPC_PARAMS) {
221 0 : int handle = ins[0]->u.ival;
222 0 : NaClLog(1, "PPB_AudioConfig_IsAudioConfig(%d)\n", handle);
223 : DataAudioConfig* data =
224 0 : GlobalAudioConfigDataResources.GetDataForHandle(handle);
225 : // for now be strict to catch problems early
226 0 : CHECK(data != NULL);
227 0 : outs[0]->u.ival = 1;
228 0 : rpc->result = NACL_SRPC_RESULT_OK;
229 0 : done->Run(done);
230 0 : }
231 :
232 : // PPB_AudioConfig_RecommendSampleFrameCount:ii:i
233 : // uint32_t RecommendSampleFrameCount(PP_AudioSampleRate sample_rate,
234 : // uint32_t requested_sample_frame_count);
235 0 : void PPB_AudioConfig_RecommendSampleFrameCount(SRPC_PARAMS) {
236 0 : int sample_frequency = ins[0]->u.ival;
237 0 : int sample_frame_count = ins[1]->u.ival;
238 : NaClLog(LOG_INFO, "PPB_AudioConfig_RecommendSampleFrameCount(%d, %d)\n",
239 0 : sample_frequency, sample_frame_count);
240 : // This is clearly imperfect.
241 : // TODO(robertm): Consider using SDL's negotiation mechanism here
242 0 : outs[0]->u.ival = kRecommendSampleFrameCount;
243 0 : rpc->result = NACL_SRPC_RESULT_OK;
244 0 : done->Run(done);
245 0 : }
246 :
247 : // PPB_AudioConfig_GetSampleRate:i:i
248 : // PP_AudioSampleRate GetSampleRate(PP_Resource config);
249 0 : void PPB_AudioConfig_GetSampleRate(SRPC_PARAMS) {
250 0 : int handle = ins[0]->u.ival;
251 0 : NaClLog(1, "PPB_AudioConfig_GetSampleRate(%d)\n", handle);
252 : DataAudioConfig* data =
253 0 : GlobalAudioConfigDataResources.GetDataForHandle(handle);
254 0 : outs[0]->u.ival = data->sample_frequency;
255 0 : rpc->result = NACL_SRPC_RESULT_OK;
256 0 : done->Run(done);
257 0 : }
258 :
259 : // PPB_AudioConfig_GetSampleFrameCount:i:i
260 : // uint32_t GetSampleFrameCount(PP_Resource config);
261 0 : void PPB_AudioConfig_GetSampleFrameCount(SRPC_PARAMS) {
262 0 : int handle = ins[0]->u.ival;
263 0 : NaClLog(1, "PPB_AudioConfig_GetSampleFrameCount(%d)\n", handle);
264 : DataAudioConfig* data =
265 0 : GlobalAudioConfigDataResources.GetDataForHandle(handle);
266 0 : outs[0]->u.ival = data->sample_frame_count;
267 0 : rpc->result = NACL_SRPC_RESULT_OK;
268 0 : done->Run(done);
269 0 : }
270 :
271 : } // namespace
272 :
273 : void InvokeAudioStreamCreatedCallback(NaClCommandLoop* ncl,
274 0 : const UserEvent *event) {
275 0 : const int handle = event->result;
276 0 : DataAudio* data = GlobalAudioDataResources.GetDataForHandle(handle);
277 : DataAudioConfig* config =
278 0 : GlobalAudioConfigDataResources.GetDataForHandle(data->handle_config);
279 : NaClSrpcArg in[NACL_SRPC_MAX_ARGS];
280 : NaClSrpcArg* ins[NACL_SRPC_MAX_ARGS + 1];
281 : NaClSrpcArg out[NACL_SRPC_MAX_ARGS];
282 : NaClSrpcArg* outs[NACL_SRPC_MAX_ARGS + 1];
283 0 : BuildArgVec(outs, out, 0);
284 0 : BuildArgVec(ins, in, 4);
285 0 : ins[0]->tag = NACL_SRPC_ARG_TYPE_INT;
286 0 : ins[0]->u.ival = handle;
287 0 : ins[1]->tag = NACL_SRPC_ARG_TYPE_HANDLE;
288 0 : ins[1]->u.hval = data->desc_shmem->desc();
289 0 : ins[2]->tag = NACL_SRPC_ARG_TYPE_INT;
290 0 : ins[2]->u.ival = config->sample_frame_count * kBytesPerSample;
291 0 : ins[3]->tag = NACL_SRPC_ARG_TYPE_HANDLE;
292 0 : ins[3]->u.hval = data->desc_sync_out->desc();
293 0 : ncl->InvokeNexeRpc("PPP_Audio_StreamCreated:ihih:", ins, outs);
294 0 : }
295 :
296 :
297 : #define TUPLE(a, b) #a #b, a
298 0 : void PepperEmuInitAudio(NaClCommandLoop* ncl, IMultimedia* im) {
299 0 : GlobalMultiMediaInterface = im;
300 0 : NaClLog(LOG_INFO, "PepperEmuInitAudio\n");
301 :
302 0 : ncl->AddUpcallRpc(TUPLE(PPB_Audio_Create, :ii:i));
303 0 : ncl->AddUpcallRpc(TUPLE(PPB_Audio_IsAudio, :i:i));
304 0 : ncl->AddUpcallRpc(TUPLE(PPB_Audio_GetCurrentConfig, :i:i));
305 0 : ncl->AddUpcallRpc(TUPLE(PPB_Audio_StopPlayback, :i:i));
306 0 : ncl->AddUpcallRpc(TUPLE(PPB_Audio_StartPlayback, :i:i));
307 :
308 0 : ncl->AddUpcallRpc(TUPLE(PPB_AudioConfig_CreateStereo16Bit, :iii:i));
309 0 : ncl->AddUpcallRpc(TUPLE(PPB_AudioConfig_IsAudioConfig, :i:i));
310 0 : ncl->AddUpcallRpc(TUPLE(PPB_AudioConfig_RecommendSampleFrameCount, :ii:i));
311 0 : ncl->AddUpcallRpc(TUPLE(PPB_AudioConfig_GetSampleRate, :i:i));
312 0 : ncl->AddUpcallRpc(TUPLE(PPB_AudioConfig_GetSampleFrameCount, :i:i));
313 22 : }
|