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"
52 int32_t bits_per_pixel
;
53 int32_t bytes_per_pixel
;
62 SDL_AudioSpec
*audio_spec
;
63 pthread_mutex_t mutex
;
64 pthread_cond_t condvar
;
67 unsigned char *stream
;
70 SDL_AudioSpec obtained
;
71 pthread_t thread_self
;
75 struct InfoMultimedia
{
76 // do not move begin (initialization dep)
77 volatile int32_t sdl_init_flags
;
78 volatile int32_t initialized
;
79 volatile int32_t have_video
;
80 volatile int32_t have_audio
;
86 static InfoMultimedia nacl_multimedia
;
87 static pthread_mutex_t nacl_multimedia_mutex
;
89 static void Fatal(const char *msg
) {
90 printf("Fatal: %s\n", msg
);
94 static int Error(const char *msg
) {
95 printf("Error: %s\n", msg
);
100 struct MultimediaModuleInit
{
101 MultimediaModuleInit();
102 ~MultimediaModuleInit();
105 MultimediaModuleInit::MultimediaModuleInit() {
106 memset(&nacl_multimedia
, 0, sizeof(nacl_multimedia
));
107 int r
= pthread_mutex_init(&nacl_multimedia_mutex
, NULL
);
109 Fatal("MultimediaModuleInit: mutex init failed\n");
112 MultimediaModuleInit::~MultimediaModuleInit() {
113 int r
= pthread_mutex_destroy(&nacl_multimedia_mutex
);
115 Fatal("~MultimediaModuleInit: mutex destroy failed\n");
116 memset(&nacl_multimedia
, 0, sizeof(nacl_multimedia
));
119 static MultimediaModuleInit multimedia_module
;
122 int nacl_multimedia_init(int subsys
) {
126 r
= pthread_mutex_lock(&nacl_multimedia_mutex
);
128 Fatal("nacl_multimedia_init: mutex lock failed");
130 // don't allow nested init
131 if (0 != nacl_multimedia
.initialized
)
132 Fatal("nacl_multimedia_init: Already initialized!");
134 if (0 != nacl_multimedia
.have_video
)
135 Fatal("nacl_multimedia_init: video initialized?");
137 if (0 != nacl_multimedia
.have_audio
)
138 Fatal("nacl_multimedia_init: audio initialized?");
140 // map nacl a/v to sdl subsystem init
141 if (NACL_SUBSYSTEM_VIDEO
== (subsys
& NACL_SUBSYSTEM_VIDEO
)) {
142 nacl_multimedia
.sdl_init_flags
|= SDL_INIT_VIDEO
;
144 if (NACL_SUBSYSTEM_AUDIO
== (subsys
& NACL_SUBSYSTEM_AUDIO
)) {
145 nacl_multimedia
.sdl_init_flags
|= SDL_INIT_AUDIO
;
147 if (SDL_Init(nacl_multimedia
.sdl_init_flags
)) {
148 Error("nacl_multimeida_init: SDL_Init failed");
153 nacl_multimedia
.initialized
= 1;
157 r
= pthread_mutex_unlock(&nacl_multimedia_mutex
);
159 Fatal("multimedia_init: mutex unlock failed");
165 int nacl_multimedia_is_embedded(int *embedded
) {
166 // standalone is never embedded in browser
172 int nacl_multimedia_get_embed_size(int *width
, int *height
) {
173 // standalone is never embedded in browser
174 // do not modify width, height
180 int nacl_multimedia_shutdown() {
183 r
= pthread_mutex_lock(&nacl_multimedia_mutex
);
185 Fatal("nacl_multimedia_shutdown: mutex lock failed");
186 if (0 == nacl_multimedia
.initialized
)
187 Fatal("nacl_multimedia_shutdown: not initialized!");
188 if (0 != nacl_multimedia
.have_video
)
189 Fatal("nacl_multimedia_shutdown: video subsystem not shutdown!");
190 if (0 != nacl_multimedia
.have_audio
)
191 Fatal("nacl_multimedia_shutdown: audio subsystem not shutdown!");
194 nacl_multimedia
.sdl_init_flags
= 0;
195 nacl_multimedia
.initialized
= 0;
197 r
= pthread_mutex_unlock(&nacl_multimedia_mutex
);
199 Fatal("nacl_multimedia_shutdown: mutex unlock failed");
204 int nacl_video_init(int width
,
208 uint32_t sdl_video_flags
= SDL_DOUBLEBUF
| SDL_HWSURFACE
;
210 r
= pthread_mutex_lock(&nacl_multimedia_mutex
);
212 Fatal("nacl_video_init: mutex lock failed");
214 if (0 == nacl_multimedia
.initialized
)
215 Fatal("nacl_video_init: multimedia not initialized");
217 // for now don't allow app to have more than one SDL window or resize
218 if (0 != nacl_multimedia
.have_video
)
219 Fatal("nacl_video_init: already initialized!");
221 if (SDL_INIT_VIDEO
!= (nacl_multimedia
.sdl_init_flags
& SDL_INIT_VIDEO
))
222 Fatal("nacl_video_init: video not originally requested");
224 // make sure width & height are not insane
225 if ((width
< kNaClVideoMinWindowSize
) ||
226 (width
> kNaClVideoMaxWindowSize
) ||
227 (height
< kNaClVideoMinWindowSize
) ||
228 (height
> kNaClVideoMaxWindowSize
)) {
229 Error("nacl_video_init: invalid window size!");
234 // width and height must also be multiples of 4
235 if ((0 != (width
& 0x3)) || (0 != (height
& 0x3))) {
236 Error("nacl_video_init: width & height must be a multiple of 4!");
241 nacl_multimedia
.have_video
= 1;
242 nacl_multimedia
.video
.screen
= SDL_SetVideoMode(width
, height
,
244 if (!nacl_multimedia
.video
.screen
) {
245 Error("nacl_video_init: SDL_SetVideoMode failed");
246 nacl_multimedia
.have_video
= 0;
251 // width, height and format validated in top half
252 nacl_multimedia
.video
.width
= width
;
253 nacl_multimedia
.video
.height
= height
;
254 nacl_multimedia
.video
.rmask
= 0x00FF0000;
255 nacl_multimedia
.video
.gmask
= 0x0000FF00;
256 nacl_multimedia
.video
.bmask
= 0x000000FF;
257 nacl_multimedia
.video
.bits_per_pixel
= 32;
258 nacl_multimedia
.video
.bytes_per_pixel
= 4;
260 /* set the window caption */
261 /* todo: as parameter to nacl_video_init? */
262 SDL_WM_SetCaption("Standalone Application", "Standalone Application");
266 r
= pthread_mutex_unlock(&nacl_multimedia_mutex
);
268 Fatal("nacl_video_init: mutex unlock failed");
273 int nacl_video_shutdown() {
277 r
= pthread_mutex_lock(&nacl_multimedia_mutex
);
279 Fatal("nacl_video_shutdown: mutex lock failed");
280 if (0 == nacl_multimedia
.initialized
)
281 Fatal("nacl_video_shutdown: multimedia not initialized!");
282 if (0 == nacl_multimedia
.have_video
) {
283 Error("nacl_video_shutdown: video already shutdown!");
287 nacl_multimedia
.have_video
= 0;
290 r
= pthread_mutex_unlock(&nacl_multimedia_mutex
);
292 Fatal("nacl_video_shutdown: mutex unlock failed");
297 int nacl_video_update(const void *data
) {
302 if (0 == nacl_multimedia
.have_video
)
303 Fatal("nacl_video_update: video not initialized");
305 image
= SDL_CreateRGBSurfaceFrom((unsigned char*) data
,
306 nacl_multimedia
.video
.width
,
307 nacl_multimedia
.video
.height
,
308 nacl_multimedia
.video
.bits_per_pixel
,
309 nacl_multimedia
.video
.width
*
310 nacl_multimedia
.video
.bytes_per_pixel
,
311 nacl_multimedia
.video
.rmask
,
312 nacl_multimedia
.video
.gmask
,
313 nacl_multimedia
.video
.bmask
, 0);
315 Error("nacl_video_update: SDL_CreateRGBSurfaceFrom failed");
320 r
= SDL_SetAlpha(image
, 0, 255);
322 Error("nacl_video_update SDL_SetAlpha failed");
324 goto done_free_image
;
327 r
= SDL_BlitSurface(image
, NULL
, nacl_multimedia
.video
.screen
, NULL
);
329 Error("nacl_video_update: SDL_BlitSurface failed");
331 goto done_free_image
;
334 r
= SDL_Flip(nacl_multimedia
.video
.screen
);
336 Error("nacl_video_update: SDL_Flip failed");
338 goto done_free_image
;
342 // fall through to free image and closure
345 SDL_FreeSurface(image
);
351 int nacl_video_poll_event(union NaClMultimediaEvent
*event
) {
357 if (0 == nacl_multimedia
.initialized
)
358 Fatal("nacl_video_poll_event: multmedia not initialized!\n");
359 if (0 == nacl_multimedia
.have_video
)
360 Fatal("nacl_video_poll_event: video not initialized");
363 sdl_r
= SDL_PollEvent(&sdl_event
);
370 switch (sdl_event
.type
) {
371 case SDL_ACTIVEEVENT
:
372 event
->type
= NACL_EVENT_ACTIVE
;
373 event
->active
.gain
= sdl_event
.active
.gain
;
374 event
->active
.state
= sdl_event
.active
.state
;
378 event
->type
= (SDL_KEYUP
== sdl_event
.type
) ?
379 NACL_EVENT_KEY_UP
: NACL_EVENT_KEY_DOWN
;
380 event
->key
.which
= sdl_event
.key
.which
;
381 event
->key
.state
= sdl_event
.key
.state
;
382 event
->key
.keysym
.scancode
= sdl_event
.key
.keysym
.scancode
;
383 event
->key
.keysym
.sym
= sdl_event
.key
.keysym
.sym
;
384 event
->key
.keysym
.mod
= sdl_event
.key
.keysym
.mod
;
385 event
->key
.keysym
.unicode
= sdl_event
.key
.keysym
.unicode
;
387 case SDL_MOUSEMOTION
:
388 event
->type
= NACL_EVENT_MOUSE_MOTION
;
389 event
->motion
.which
= sdl_event
.motion
.which
;
390 event
->motion
.state
= sdl_event
.motion
.state
;
391 event
->motion
.x
= sdl_event
.motion
.x
;
392 event
->motion
.y
= sdl_event
.motion
.y
;
393 event
->motion
.xrel
= sdl_event
.motion
.xrel
;
394 event
->motion
.yrel
= sdl_event
.motion
.yrel
;
396 case SDL_MOUSEBUTTONDOWN
:
397 case SDL_MOUSEBUTTONUP
:
398 event
->type
= (SDL_MOUSEBUTTONUP
== sdl_event
.type
) ?
399 NACL_EVENT_MOUSE_BUTTON_UP
:
400 NACL_EVENT_MOUSE_BUTTON_DOWN
;
401 event
->button
.which
= sdl_event
.button
.which
;
402 event
->button
.button
= sdl_event
.button
.button
;
403 event
->button
.state
= sdl_event
.button
.state
;
404 event
->button
.x
= sdl_event
.button
.x
;
405 event
->button
.y
= sdl_event
.button
.y
;
408 event
->type
= NACL_EVENT_QUIT
;
411 // an sdl event happened, but we don't support it
412 // so move along and repoll for another event
417 } while (0 != repoll
);
423 void __NaCl_InternalAudioCallback(void *unused
, Uint8
*stream
, int size
) {
426 r
= pthread_mutex_lock(&nacl_multimedia
.audio
.mutex
);
428 Fatal("__NaCl_InternalAudioCallback: mutex lock failed");
429 if (0 == nacl_multimedia
.audio
.ready
) {
430 nacl_multimedia
.audio
.stream
= stream
;
431 nacl_multimedia
.audio
.size
= size
;
432 nacl_multimedia
.audio
.ready
= 1;
433 r
= pthread_cond_signal(&nacl_multimedia
.audio
.condvar
);
435 Fatal("__NaCl_InternalAudioCallback: cond var signal failed");
436 // wait for return signal
437 while ((1 == nacl_multimedia
.audio
.ready
) &&
438 (0 == nacl_multimedia
.audio
.shutdown
)) {
439 r
= pthread_cond_wait(&nacl_multimedia
.audio
.condvar
,
440 &nacl_multimedia
.audio
.mutex
);
442 Fatal("__NaCl_InternalAudioCallback: cond var wait failed");
445 r
= pthread_mutex_unlock(&nacl_multimedia
.audio
.mutex
);
447 Fatal("__NaCl_InternalAudioCallback: mutex unlock failed");
451 int nacl_audio_init(enum NaClAudioFormat format
,
453 int *obtained_samples
) {
454 SDL_AudioSpec desired
;
458 r
= pthread_mutex_lock(&nacl_multimedia_mutex
);
460 Fatal("nacl_audio_init: mutex lock failed");
462 if (0 == nacl_multimedia
.initialized
)
463 Fatal("nacl_audio_init: multimedia not initialized!");
465 // for now, don't allow an app to open more than one SDL audio device
466 if (0 != nacl_multimedia
.have_audio
)
467 Fatal("nacl_audio_init: already initialized!");
469 if ((desired_samples
< 128) || (desired_samples
> 8192)) {
470 Error("nacl_audio_init: desired sample value out of range");
475 memset(&nacl_multimedia
.audio
, 0, sizeof(nacl_multimedia
.audio
));
476 memset(&desired
, 0, sizeof(desired
));
478 r
= pthread_mutex_init(&nacl_multimedia
.audio
.mutex
, NULL
);
480 Fatal("nacl_audio_init: mutex init failed");
481 r
= pthread_cond_init(&nacl_multimedia
.audio
.condvar
, NULL
);
483 Fatal("nacl_audio_init: cond var ctor failed");
485 nacl_multimedia
.audio
.thread_self
= 0;
486 nacl_multimedia
.audio
.size
= 0;
487 nacl_multimedia
.audio
.ready
= 0;
488 nacl_multimedia
.audio
.first
= 1;
489 nacl_multimedia
.audio
.shutdown
= 0;
491 desired
.format
= AUDIO_S16LSB
;
492 desired
.channels
= 2;
493 desired
.samples
= desired_samples
;
494 desired
.callback
= __NaCl_InternalAudioCallback
;
496 if (NACL_AUDIO_FORMAT_STEREO_44K
== format
) {
497 desired
.freq
= 44100;
498 } else if (NACL_AUDIO_FORMAT_STEREO_48K
== format
) {
499 desired
.freq
= 48000;
501 // we only support two simple high quality stereo formats
502 Error("nacl_audio_init: unsupported format");
507 if (SDL_OpenAudio(&desired
, &nacl_multimedia
.audio
.obtained
) < 0) {
508 Error("nacl_audio_init: Couldn't open SDL audio");
509 Error(SDL_GetError());
514 if (nacl_multimedia
.audio
.obtained
.format
!= desired
.format
) {
515 Error("nacl_audio_init: Couldn't get desired format");
520 *obtained_samples
= nacl_multimedia
.audio
.obtained
.samples
;
522 nacl_multimedia
.have_audio
= 1;
530 r
= pthread_mutex_unlock(&nacl_multimedia_mutex
);
532 Fatal("nacl_audio_init: mutex unlock failed");
538 int nacl_audio_shutdown() {
541 r
= pthread_mutex_lock(&nacl_multimedia_mutex
);
543 Fatal("nacl_audio_shutdown: mutex lock failed");
544 if (0 == nacl_multimedia
.initialized
)
545 Fatal("nacl_audio_shutdown: multimedia not initialized");
546 if (0 == nacl_multimedia
.have_audio
)
547 Fatal("nacl_audio_shutdown: audio not initialized!");
548 // tell audio thread we're shutting down
549 r
= pthread_mutex_lock(&nacl_multimedia
.audio
.mutex
);
551 Fatal("nacl_audio_shutdown: mutex lock failed");
553 nacl_multimedia
.audio
.shutdown
= 1;
554 r
= pthread_cond_broadcast(&nacl_multimedia
.audio
.condvar
);
556 Fatal("nacl_audio_shutdown: cond var broadcast failed");
557 r
= pthread_mutex_unlock(&nacl_multimedia
.audio
.mutex
);
559 Fatal("nacl_audio_shutdown: mutex unlock failed");
562 // no more callbacks at this point
563 nacl_multimedia
.have_audio
= 0;
564 r
= pthread_mutex_unlock(&nacl_multimedia_mutex
);
566 Fatal("nacl_audio_shutdown: mutex unlock failed");
572 // returns 0 during normal operation.
573 // returns -1 indicating that it is time to exit the audio thread
574 int32_t nacl_audio_stream(const void *data
,
579 r
= pthread_mutex_lock(&nacl_multimedia_mutex
);
581 Fatal("nacl_audio_stream: global mutex lock failed");
583 if (0 == nacl_multimedia
.have_audio
) {
587 if (nacl_multimedia
.audio
.first
) {
588 // don't copy data on first call...
589 nacl_multimedia
.audio
.thread_self
= pthread_self();
591 if (nacl_multimedia
.audio
.thread_self
!= pthread_self()) {
592 Fatal("nacl_audio_stream: called from different thread");
595 // copy the audio data into the sdl audio buffer
596 memcpy(nacl_multimedia
.audio
.stream
, data
, nacl_multimedia
.audio
.size
);
599 // callback synchronization
600 r
= pthread_mutex_lock(&nacl_multimedia
.audio
.mutex
);
602 Fatal("nacl_audio_stream: audio mutex lock failed");
603 // unpause audio on first, start callbacks
604 if (nacl_multimedia
.audio
.first
) {
606 nacl_multimedia
.audio
.first
= 0;
609 // signal callback (if it is waiting)
610 if (nacl_multimedia
.audio
.ready
!= 0) {
611 nacl_multimedia
.audio
.ready
= 0;
612 r
= pthread_cond_signal(&nacl_multimedia
.audio
.condvar
);
614 Fatal("nacl_audio_stream: cond var signal failed");
617 nacl_multimedia
.audio
.size
= 0;
619 // wait for next callback
620 while ((0 == nacl_multimedia
.audio
.ready
) &&
621 (0 == nacl_multimedia
.audio
.shutdown
)) {
622 r
= pthread_cond_wait(&nacl_multimedia
.audio
.condvar
,
623 &nacl_multimedia
.audio
.mutex
);
625 Fatal("nacl_audio_stream: cond var wait failed");
628 if (0 != nacl_multimedia
.audio
.shutdown
) {
629 nacl_multimedia
.audio
.size
= 0;
633 // return size of next audio block
634 *size
= (size_t)nacl_multimedia
.audio
.size
;
635 r
= pthread_mutex_unlock(&nacl_multimedia
.audio
.mutex
);
637 Fatal("nacl_audio_stream: audio mutex unlock failed");
641 r
= pthread_mutex_unlock(&nacl_multimedia_mutex
);
643 Fatal("nacl_audio_stream: global mutex unlock failed");