2 * Copyright 2008, Google Inc.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
15 * * Neither the name of Google Inc. nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 // Standalone helper functions
33 // Mimic barebones nacl multimedia interface for standalone
34 // non-nacl applications. Mainly used as a debugging aid.
35 // * Currently standalone is only supported under Linux
37 #if defined(STANDALONE)
45 #include "./standalone.h"
47 const int kMinScreenSize
= 32;
48 const int kMaxScreenSize
= 4096;
54 int32_t bits_per_pixel
;
55 int32_t bytes_per_pixel
;
64 SDL_AudioSpec
*audio_spec
;
65 pthread_mutex_t mutex
;
66 pthread_cond_t condvar
;
69 unsigned char *stream
;
72 SDL_AudioSpec obtained
;
73 pthread_t thread_self
;
77 struct InfoMultimedia
{
78 // do not move begin (initialization dep)
79 volatile int32_t sdl_init_flags
;
80 volatile int32_t initialized
;
81 volatile int32_t have_video
;
82 volatile int32_t have_audio
;
88 static InfoMultimedia nacl_multimedia
;
89 static pthread_mutex_t nacl_multimedia_mutex
;
91 static void Fatal(const char *msg
) {
92 printf("Fatal: %s\n", msg
);
96 static int Error(const char *msg
) {
97 printf("Error: %s\n", msg
);
102 struct MultimediaModuleInit
{
103 MultimediaModuleInit();
104 ~MultimediaModuleInit();
107 MultimediaModuleInit::MultimediaModuleInit() {
108 memset(&nacl_multimedia
, 0, sizeof(nacl_multimedia
));
109 int r
= pthread_mutex_init(&nacl_multimedia_mutex
, NULL
);
111 Fatal("MultimediaModuleInit: mutex init failed\n");
114 MultimediaModuleInit::~MultimediaModuleInit() {
115 int r
= pthread_mutex_destroy(&nacl_multimedia_mutex
);
117 Fatal("~MultimediaModuleInit: mutex destroy failed\n");
118 memset(&nacl_multimedia
, 0, sizeof(nacl_multimedia
));
121 static MultimediaModuleInit multimedia_module
;
124 int nacl_multimedia_init(int subsys
) {
128 r
= pthread_mutex_lock(&nacl_multimedia_mutex
);
130 Fatal("nacl_multimedia_init: mutex lock failed");
132 // don't allow nested init
133 if (0 != nacl_multimedia
.initialized
)
134 Fatal("nacl_multimedia_init: Already initialized!");
136 if (0 != nacl_multimedia
.have_video
)
137 Fatal("nacl_multimedia_init: video initialized?");
139 if (0 != nacl_multimedia
.have_audio
)
140 Fatal("nacl_multimedia_init: audio initialized?");
142 // map nacl a/v to sdl subsystem init
143 if (NACL_SUBSYSTEM_VIDEO
== (subsys
& NACL_SUBSYSTEM_VIDEO
)) {
144 nacl_multimedia
.sdl_init_flags
|= SDL_INIT_VIDEO
;
146 if (NACL_SUBSYSTEM_AUDIO
== (subsys
& NACL_SUBSYSTEM_AUDIO
)) {
147 nacl_multimedia
.sdl_init_flags
|= SDL_INIT_AUDIO
;
149 if (SDL_Init(nacl_multimedia
.sdl_init_flags
)) {
150 Error("nacl_multimeida_init: SDL_Init failed");
155 nacl_multimedia
.initialized
= 1;
159 r
= pthread_mutex_unlock(&nacl_multimedia_mutex
);
161 Fatal("multimedia_init: mutex unlock failed");
167 int nacl_multimedia_shutdown() {
170 r
= pthread_mutex_lock(&nacl_multimedia_mutex
);
172 Fatal("nacl_multimedia_shutdown: mutex lock failed");
173 if (0 == nacl_multimedia
.initialized
)
174 Fatal("nacl_multimedia_shutdown: not initialized!");
175 if (0 != nacl_multimedia
.have_video
)
176 Fatal("nacl_multimedia_shutdown: video subsystem not shutdown!");
177 if (0 != nacl_multimedia
.have_audio
)
178 Fatal("nacl_multimedia_shutdown: audio subsystem not shutdown!");
181 nacl_multimedia
.sdl_init_flags
= 0;
182 nacl_multimedia
.initialized
= 0;
184 r
= pthread_mutex_unlock(&nacl_multimedia_mutex
);
186 Fatal("nacl_multimedia_shutdown: mutex unlock failed");
191 int nacl_video_init(enum NaClVideoFormat format
,
196 uint32_t sdl_video_flags
= SDL_DOUBLEBUF
| SDL_HWSURFACE
;
198 r
= pthread_mutex_lock(&nacl_multimedia_mutex
);
200 Fatal("nacl_video_init: mutex lock failed");
202 if (0 == nacl_multimedia
.initialized
)
203 Fatal("nacl_video_init: multimedia not initialized");
205 // for now don't allow app to have more than one SDL window or resize
206 if (0 != nacl_multimedia
.have_video
)
207 Fatal("nacl_video_init: already initialized!");
209 if (SDL_INIT_VIDEO
!= (nacl_multimedia
.sdl_init_flags
& SDL_INIT_VIDEO
))
210 Fatal("nacl_video_init: video not originally requested");
212 // make sure width & height are not insane
213 if ((width
< kMinScreenSize
) || (width
> kMaxScreenSize
) ||
214 (height
< kMinScreenSize
) || (height
> kMaxScreenSize
)) {
215 Error("nacl_video_init: invalid screen size!");
220 // width and height must also be multiples of 4
221 if ((0 != (width
& 0x3)) || (0 != (height
& 0x3))) {
222 Error("nacl_video_init: width & height must be a multiple of 4!");
227 // make sure video format is valid
228 if ((format
!= NACL_VIDEO_FORMAT_RGB
) &&
229 (format
!= NACL_VIDEO_FORMAT_RGBA
) &&
230 (format
!= NACL_VIDEO_FORMAT_BGRA
)) {
231 Error("nacl_video_init: invalid format parameter\n");
236 nacl_multimedia
.have_video
= 1;
237 nacl_multimedia
.video
.screen
= SDL_SetVideoMode(width
, height
,
239 if (!nacl_multimedia
.video
.screen
) {
240 Error("nacl_video_init: SDL_SetVideoMode failed");
241 nacl_multimedia
.have_video
= 0;
246 // width, height and format validated in top half
247 nacl_multimedia
.video
.format
= format
;
248 nacl_multimedia
.video
.width
= width
;
249 nacl_multimedia
.video
.height
= height
;
250 if (NACL_VIDEO_FORMAT_RGB
== format
) {
251 nacl_multimedia
.video
.rmask
= 0x000000FF;
252 nacl_multimedia
.video
.gmask
= 0x0000FF00;
253 nacl_multimedia
.video
.bmask
= 0x00FF0000;
254 nacl_multimedia
.video
.bits_per_pixel
= 24;
255 nacl_multimedia
.video
.bytes_per_pixel
= 3;
256 } else if (NACL_VIDEO_FORMAT_RGBA
== format
) {
257 nacl_multimedia
.video
.rmask
= 0x000000FF;
258 nacl_multimedia
.video
.gmask
= 0x0000FF00;
259 nacl_multimedia
.video
.bmask
= 0x00FF0000;
260 nacl_multimedia
.video
.bits_per_pixel
= 32;
261 nacl_multimedia
.video
.bytes_per_pixel
= 4;
262 } else if (NACL_VIDEO_FORMAT_BGRA
== format
) {
263 nacl_multimedia
.video
.rmask
= 0x00FF0000;
264 nacl_multimedia
.video
.gmask
= 0x0000FF00;
265 nacl_multimedia
.video
.bmask
= 0x000000FF;
266 nacl_multimedia
.video
.bits_per_pixel
= 32;
267 nacl_multimedia
.video
.bytes_per_pixel
= 4;
269 Fatal("nacl_video_init: Panic, unrecognized format!");
272 /* set the window caption */
273 /* todo: as parameter to nacl_video_init? */
274 SDL_WM_SetCaption("Standalone Application", "Standalone Application");
278 r
= pthread_mutex_unlock(&nacl_multimedia_mutex
);
280 Fatal("nacl_video_init: mutex unlock failed");
285 int nacl_video_shutdown() {
289 r
= pthread_mutex_lock(&nacl_multimedia_mutex
);
291 Fatal("nacl_video_shutdown: mutex lock failed");
292 if (0 == nacl_multimedia
.initialized
)
293 Fatal("nacl_video_shutdown: multimedia not initialized!");
294 if (0 == nacl_multimedia
.have_video
) {
295 Error("nacl_video_shutdown: video already shutdown!");
299 nacl_multimedia
.have_video
= 0;
302 r
= pthread_mutex_unlock(&nacl_multimedia_mutex
);
304 Fatal("nacl_video_shutdown: mutex unlock failed");
309 int nacl_video_update(const void *data
) {
314 if (0 == nacl_multimedia
.have_video
)
315 Fatal("nacl_video_update: video not initialized");
317 image
= SDL_CreateRGBSurfaceFrom((unsigned char*) data
,
318 nacl_multimedia
.video
.width
,
319 nacl_multimedia
.video
.height
,
320 nacl_multimedia
.video
.bits_per_pixel
,
321 nacl_multimedia
.video
.width
*
322 nacl_multimedia
.video
.bytes_per_pixel
,
323 nacl_multimedia
.video
.rmask
,
324 nacl_multimedia
.video
.gmask
,
325 nacl_multimedia
.video
.bmask
, 0);
327 Error("nacl_video_update: SDL_CreateRGBSurfaceFrom failed");
332 r
= SDL_SetAlpha(image
, 0, 255);
334 Error("nacl_video_update SDL_SetAlpha failed");
336 goto done_free_image
;
339 r
= SDL_BlitSurface(image
, NULL
, nacl_multimedia
.video
.screen
, NULL
);
341 Error("nacl_video_update: SDL_BlitSurface failed");
343 goto done_free_image
;
346 r
= SDL_Flip(nacl_multimedia
.video
.screen
);
348 Error("nacl_video_update: SDL_Flip failed");
350 goto done_free_image
;
354 // fall through to free image and closure
357 SDL_FreeSurface(image
);
363 int nacl_video_poll_event(union NaClMultimediaEvent
*event
) {
369 if (0 == nacl_multimedia
.initialized
)
370 Fatal("nacl_video_poll_event: multmedia not initialized!\n");
371 if (0 == nacl_multimedia
.have_video
)
372 Fatal("nacl_video_poll_event: video not initialized");
375 sdl_r
= SDL_PollEvent(&sdl_event
);
382 switch (sdl_event
.type
) {
383 case SDL_ACTIVEEVENT
:
384 event
->type
= NACL_EVENT_ACTIVE
;
385 event
->active
.gain
= sdl_event
.active
.gain
;
386 event
->active
.state
= sdl_event
.active
.state
;
390 event
->type
= (SDL_KEYUP
== sdl_event
.type
) ?
391 NACL_EVENT_KEY_UP
: NACL_EVENT_KEY_DOWN
;
392 event
->key
.which
= sdl_event
.key
.which
;
393 event
->key
.state
= sdl_event
.key
.state
;
394 event
->key
.keysym
.scancode
= sdl_event
.key
.keysym
.scancode
;
395 event
->key
.keysym
.sym
= sdl_event
.key
.keysym
.sym
;
396 event
->key
.keysym
.mod
= sdl_event
.key
.keysym
.mod
;
397 event
->key
.keysym
.unicode
= sdl_event
.key
.keysym
.unicode
;
399 case SDL_MOUSEMOTION
:
400 event
->type
= NACL_EVENT_MOUSE_MOTION
;
401 event
->motion
.which
= sdl_event
.motion
.which
;
402 event
->motion
.state
= sdl_event
.motion
.state
;
403 event
->motion
.x
= sdl_event
.motion
.x
;
404 event
->motion
.y
= sdl_event
.motion
.y
;
405 event
->motion
.xrel
= sdl_event
.motion
.xrel
;
406 event
->motion
.yrel
= sdl_event
.motion
.yrel
;
408 case SDL_MOUSEBUTTONDOWN
:
409 case SDL_MOUSEBUTTONUP
:
410 event
->type
= (SDL_MOUSEBUTTONUP
== sdl_event
.type
) ?
411 NACL_EVENT_MOUSE_BUTTON_UP
:
412 NACL_EVENT_MOUSE_BUTTON_DOWN
;
413 event
->button
.which
= sdl_event
.button
.which
;
414 event
->button
.button
= sdl_event
.button
.button
;
415 event
->button
.state
= sdl_event
.button
.state
;
416 event
->button
.x
= sdl_event
.button
.x
;
417 event
->button
.y
= sdl_event
.button
.y
;
420 event
->type
= NACL_EVENT_QUIT
;
423 // an sdl event happened, but we don't support it
424 // so move along and repoll for another event
429 } while (0 != repoll
);
435 void __NaCl_InternalAudioCallback(void *unused
, Uint8
*stream
, int size
) {
438 r
= pthread_mutex_lock(&nacl_multimedia
.audio
.mutex
);
440 Fatal("__NaCl_InternalAudioCallback: mutex lock failed");
441 if (0 == nacl_multimedia
.audio
.ready
) {
442 nacl_multimedia
.audio
.stream
= stream
;
443 nacl_multimedia
.audio
.size
= size
;
444 nacl_multimedia
.audio
.ready
= 1;
445 r
= pthread_cond_signal(&nacl_multimedia
.audio
.condvar
);
447 Fatal("__NaCl_InternalAudioCallback: cond var signal failed");
448 // wait for return signal
449 while ((1 == nacl_multimedia
.audio
.ready
) &&
450 (0 == nacl_multimedia
.audio
.shutdown
)) {
451 r
= pthread_cond_wait(&nacl_multimedia
.audio
.condvar
,
452 &nacl_multimedia
.audio
.mutex
);
454 Fatal("__NaCl_InternalAudioCallback: cond var wait failed");
457 r
= pthread_mutex_unlock(&nacl_multimedia
.audio
.mutex
);
459 Fatal("__NaCl_InternalAudioCallback: mutex unlock failed");
463 int nacl_audio_init(enum NaClAudioFormat format
,
465 int *obtained_samples
) {
466 SDL_AudioSpec desired
;
470 r
= pthread_mutex_lock(&nacl_multimedia_mutex
);
472 Fatal("nacl_audio_init: mutex lock failed");
474 if (0 == nacl_multimedia
.initialized
)
475 Fatal("nacl_audio_init: multimedia not initialized!");
477 // for now, don't allow an app to open more than one SDL audio device
478 if (0 != nacl_multimedia
.have_audio
)
479 Fatal("nacl_audio_init: already initialized!");
481 if ((desired_samples
< 128) || (desired_samples
> 8192)) {
482 Error("nacl_audio_init: desired sample value out of range");
487 memset(&nacl_multimedia
.audio
, 0, sizeof(nacl_multimedia
.audio
));
488 memset(&desired
, 0, sizeof(desired
));
490 r
= pthread_mutex_init(&nacl_multimedia
.audio
.mutex
, NULL
);
492 Fatal("nacl_audio_init: mutex init failed");
493 r
= pthread_cond_init(&nacl_multimedia
.audio
.condvar
, NULL
);
495 Fatal("nacl_audio_init: cond var ctor failed");
497 nacl_multimedia
.audio
.thread_self
= 0;
498 nacl_multimedia
.audio
.size
= 0;
499 nacl_multimedia
.audio
.ready
= 0;
500 nacl_multimedia
.audio
.first
= 1;
501 nacl_multimedia
.audio
.shutdown
= 0;
503 desired
.format
= AUDIO_S16LSB
;
504 desired
.channels
= 2;
505 desired
.samples
= desired_samples
;
506 desired
.callback
= __NaCl_InternalAudioCallback
;
508 if (NACL_AUDIO_FORMAT_STEREO_44K
== format
) {
509 desired
.freq
= 44100;
510 } else if (NACL_AUDIO_FORMAT_STEREO_48K
== format
) {
511 desired
.freq
= 48000;
513 // we only support two simple high quality stereo formats
514 Error("nacl_audio_init: unsupported format");
519 if (SDL_OpenAudio(&desired
, &nacl_multimedia
.audio
.obtained
) < 0) {
520 Error("nacl_audio_init: Couldn't open SDL audio");
521 Error(SDL_GetError());
526 if (nacl_multimedia
.audio
.obtained
.format
!= desired
.format
) {
527 Error("nacl_audio_init: Couldn't get desired format");
532 *obtained_samples
= nacl_multimedia
.audio
.obtained
.samples
;
534 nacl_multimedia
.have_audio
= 1;
542 r
= pthread_mutex_unlock(&nacl_multimedia_mutex
);
544 Fatal("nacl_audio_init: mutex unlock failed");
550 int nacl_audio_shutdown() {
553 r
= pthread_mutex_lock(&nacl_multimedia_mutex
);
555 Fatal("nacl_audio_shutdown: mutex lock failed");
556 if (0 == nacl_multimedia
.initialized
)
557 Fatal("nacl_audio_shutdown: multimedia not initialized");
558 if (0 == nacl_multimedia
.have_audio
)
559 Fatal("nacl_audio_shutdown: audio not initialized!");
560 // tell audio thread we're shutting down
561 r
= pthread_mutex_lock(&nacl_multimedia
.audio
.mutex
);
563 Fatal("nacl_audio_shutdown: mutex lock failed");
565 nacl_multimedia
.audio
.shutdown
= 1;
566 r
= pthread_cond_broadcast(&nacl_multimedia
.audio
.condvar
);
568 Fatal("nacl_audio_shutdown: cond var broadcast failed");
569 r
= pthread_mutex_unlock(&nacl_multimedia
.audio
.mutex
);
571 Fatal("nacl_audio_shutdown: mutex unlock failed");
574 // no more callbacks at this point
575 nacl_multimedia
.have_audio
= 0;
576 r
= pthread_mutex_unlock(&nacl_multimedia_mutex
);
578 Fatal("nacl_audio_shutdown: mutex unlock failed");
584 // returns 0 during normal operation.
585 // returns -1 indicating that it is time to exit the audio thread
586 int32_t nacl_audio_stream(const void *data
,
591 r
= pthread_mutex_lock(&nacl_multimedia_mutex
);
593 Fatal("nacl_audio_stream: global mutex lock failed");
595 if (0 == nacl_multimedia
.have_audio
) {
599 if (nacl_multimedia
.audio
.first
) {
600 // don't copy data on first call...
601 nacl_multimedia
.audio
.thread_self
= pthread_self();
603 if (nacl_multimedia
.audio
.thread_self
!= pthread_self()) {
604 Fatal("nacl_audio_stream: called from different thread");
607 // copy the audio data into the sdl audio buffer
608 memcpy(nacl_multimedia
.audio
.stream
, data
, nacl_multimedia
.audio
.size
);
611 // callback synchronization
612 r
= pthread_mutex_lock(&nacl_multimedia
.audio
.mutex
);
614 Fatal("nacl_audio_stream: audio mutex lock failed");
615 // unpause audio on first, start callbacks
616 if (nacl_multimedia
.audio
.first
) {
618 nacl_multimedia
.audio
.first
= 0;
621 // signal callback (if it is waiting)
622 if (nacl_multimedia
.audio
.ready
!= 0) {
623 nacl_multimedia
.audio
.ready
= 0;
624 r
= pthread_cond_signal(&nacl_multimedia
.audio
.condvar
);
626 Fatal("nacl_audio_stream: cond var signal failed");
629 nacl_multimedia
.audio
.size
= 0;
631 // wait for next callback
632 while ((0 == nacl_multimedia
.audio
.ready
) &&
633 (0 == nacl_multimedia
.audio
.shutdown
)) {
634 r
= pthread_cond_wait(&nacl_multimedia
.audio
.condvar
,
635 &nacl_multimedia
.audio
.mutex
);
637 Fatal("nacl_audio_stream: cond var wait failed");
640 if (0 != nacl_multimedia
.audio
.shutdown
) {
641 nacl_multimedia
.audio
.size
= 0;
645 // return size of next audio block
646 *size
= (size_t)nacl_multimedia
.audio
.size
;
647 r
= pthread_mutex_unlock(&nacl_multimedia
.audio
.mutex
);
649 Fatal("nacl_audio_stream: audio mutex unlock failed");
653 r
= pthread_mutex_unlock(&nacl_multimedia_mutex
);
655 Fatal("nacl_audio_stream: global mutex unlock failed");