summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2007-09-03 23:31:39 +0200
committerLennart Poettering <lennart@poettering.net>2007-09-03 23:31:39 +0200
commitcbc2fafc391ecdc141d016e8f7176847e43ba212 (patch)
tree45e03dffdeb355f46e9b6f4f8dc93c7b2ddaf31b
Initial version
-rw-r--r--Makefile8
-rw-r--r--flashsupport.c1218
2 files changed, 1226 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..ed66e29
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,8 @@
+CFLAGS=-O0 -Wall -Wextra -W `pkg-config --cflags libpulse` -g
+LIBS=-lssl `pkg-config --libs libpulse`
+
+libflashsupport.so: flashsupport.c
+ $(CC) -shared $(CFLAGS) $(LIBS) $< -o $@
+
+clean:
+ rm -f *.so
diff --git a/flashsupport.c b/flashsupport.c
new file mode 100644
index 0000000..141079d
--- /dev/null
+++ b/flashsupport.c
@@ -0,0 +1,1218 @@
+/*
+ Adobe Systems Incorporated(r) Source Code License Agreement
+ Copyright(c) 2006 Adobe Systems Incorporated. All rights reserved.
+
+ Please read this Source Code License Agreement carefully before using
+ the source code.
+
+ Adobe Systems Incorporated grants to you a perpetual, worldwide, non-exclusive,
+ no-charge, royalty-free, irrevocable copyright license, to reproduce,
+ prepare derivative works of, publicly display, publicly perform, and
+ distribute this source code and such derivative works in source or
+ object code form without any attribution requirements.
+
+ The name "Adobe Systems Incorporated" must not be used to endorse or promote products
+ derived from the source code without prior written permission.
+
+ You agree to indemnify, hold harmless and defend Adobe Systems Incorporated from and
+ against any loss, damage, claims or lawsuits, including attorney's
+ fees that arise or result from your use or distribution of the source
+ code.
+
+ THIS SOURCE CODE IS PROVIDED "AS IS" AND "WITH ALL FAULTS", WITHOUT
+ ANY TECHNICAL SUPPORT OR ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
+ BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ALSO, THERE IS NO WARRANTY OF
+ NON-INFRINGEMENT, TITLE OR QUIET ENJOYMENT. IN NO EVENT SHALL MACROMEDIA
+ OR ITS SUPPLIERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOURCE CODE, EVEN IF
+ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// File name: flashsupport.c
+// Targets: Adobe Flash Player 9.1 beta 2 for Linux (9.0.21.78)
+// Last Revision Date: 11/20/2006
+//
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// These are the feature which can be turned on and off. They are all
+// optional. The ALSA and Video4Linux support in this file is somewhat
+// redundant and reduced in functionality as the Flash Player already has
+// ALSA and Video4Linux support built-in. It is provided here for reference only.
+// Also, if your system has ALSA support in the kernel there is no need to
+// enable OSS here as it will override it.
+//
+
+#define OPENSSL
+//#define GNUTLS
+//#define ALSA
+//#define OSS
+#define V4L1
+#define PULSEAUDIO
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// To compile and install flashsupport.c the following components are required:
+//
+// - a C compiler (gcc 4.03 is known to be working)
+// - OpenSSL developer package and working user libraries (OpenSSL 0.9.8 is known to be working)
+// - OSS (or ALSA) developer package and working users libraries (Linux 2.6.15 is known to be working)
+// - sudo or root access to install the generated library to /usr/lib
+//
+// We suggest these steps in a terminal:
+//
+// > cc -shared -O2 -Wall -Werror -lssl flashsupport.c -o libflashsupport.so
+// > ldd libflashplayer.so
+// > sudo cp libflashplayer.so /usr/lib
+//
+// Make sure that 'ldd' can resolve all dynamic libraries. Otherwise the Flash Player
+// will silently fail to load and use libflashsupport.so.
+//
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// SHARED SECTION, DO NOT CHANGE!
+//
+#ifdef cplusplus
+extern "C" {
+#endif // cplusplus
+
+//
+// Imported functions
+//
+
+ typedef void *(*T_FPI_Mem_Alloc)(int size); // This function is not thread safe
+ typedef void (*T_FPI_Mem_Free)(void *ptr); // This function is not thread safe
+
+#if defined(ALSA) || defined(OSS) || defined(PULSEAUDIO)
+ typedef void (*T_FPI_SoundOutput_FillBuffer)(void *ptr, char *buffer, int n_bytes); // This function is thread safe
+#endif // defined(ALSA) || defined(OSS)
+
+ struct FPI_Functions {
+ int fpi_count;
+ void *fpi_mem_alloc; // 1
+ void *fpi_mem_free; // 2
+ void *fpi_soundoutput_fillbuffer; // 3
+ };
+
+//
+// Exported functions
+//
+
+ void *FPX_Init(void *ptr);
+
+ static void FPX_Shutdown(void);
+
+#if defined(OPENSSL) || defined(GNUTLS)
+ static void *FPX_SSLSocket_Create(int socket_fd);
+ static int FPX_SSLSocket_Destroy(void *ptr);
+ static int FPX_SSLSocket_Connect(void *ptr);
+ static int FPX_SSLSocket_Receive(void *ptr, char *buffer, int n_bytes);
+ static int FPX_SSLSocket_Send(void *ptr, const char *buffer, int n_bytes);
+#endif // defined(OPENSSL) || defined(GNUTLS)
+
+#if defined(ALSA) || defined(OSS) || defined(PULSEAUDIO)
+ static void *FPX_SoundOutput_Open(void);
+ static int FPX_SoundOutput_Close(void *ptr);
+ static int FPX_SoundOutput_Latency(void *ptr);
+#endif // defined(ALSA) || defined(OSS)
+
+#ifdef V4L1
+ static void *FPX_VideoInput_Open();
+ static int FPX_VideoInput_Close(void *ptr);
+ static int FPX_VideoInput_GetFrame(void *ptr, char *data, int width, int height, int pitch_n_bytes);
+#endif // V4L1
+
+ struct FPX_Functions {
+ int fpx_count;
+ void *fpx_shutdown; // 1
+ void *fpx_sslsocket_create; // 2
+ void *fpx_sslsocket_destroy; // 3
+ void *fpx_sslsocket_connect; // 4
+ void *fpx_sslsocket_receive; // 5
+ void *fpx_sslsocket_send; // 6
+ void *fpx_soundoutput_open; // 7
+ void *fpx_soundoutput_close; // 8
+ void *fpx_soundoutput_latency; // 9
+ void *fpx_videoinput_open; // 10
+ void *fpx_videoinput_close; // 11
+ void *fpx_videoinput_getframe; // 12
+ };
+
+#ifdef cplusplus
+};
+#endif // cplusplus
+//
+// END OF SHARED SECTION
+//
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+#include <memory.h>
+
+#ifdef OPENSSL
+#include <openssl/ssl.h>
+#elif defined(GNUTLS)
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <gnutls/gnutls.h>
+#endif // GNUTLS
+
+#ifdef ALSA
+#include <pthread.h>
+#include <semaphore.h>
+#include <alsa/asoundlib.h>
+#elif defined(OSS)
+#include <unistd.h>
+#include <pthread.h>
+#include <linux/soundcard.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#endif // OSS
+
+#ifdef V4L1
+#include <unistd.h>
+#include <pthread.h>
+#include <linux/videodev.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#endif // V4L1
+
+#ifdef PULSEAUDIO
+#include <pulse/pulseaudio.h>
+#endif
+
+
+static struct FPX_Functions fpx_functions;
+
+static T_FPI_Mem_Alloc FPI_Mem_Alloc = 0;
+static T_FPI_Mem_Free FPI_Mem_Free = 0;
+
+#if defined(ALSA) || defined(OSS) || defined(PULSEAUDIO)
+static T_FPI_SoundOutput_FillBuffer FPI_SoundOutput_FillBuffer = 0;
+#endif // defined(ALSA) || defined(OSS)
+
+void *FPX_Init(void *ptr)
+{
+ if ( !ptr ) return 0;
+
+ //
+ // Setup imported functions
+ //
+
+ struct FPI_Functions *fpi_functions = (struct FPI_Functions *)ptr;
+
+ if ( fpi_functions->fpi_count >= 1 ) FPI_Mem_Alloc = fpi_functions->fpi_mem_alloc; // 1
+ if ( fpi_functions->fpi_count >= 2 ) FPI_Mem_Free = fpi_functions->fpi_mem_free; // 2
+
+#if defined(ALSA) || defined(OSS) || defined(PULSEAUDIO)
+ if ( fpi_functions->fpi_count >= 3 ) FPI_SoundOutput_FillBuffer= fpi_functions->fpi_soundoutput_fillbuffer; // 3
+#endif // defined(ALSA) || defined(OSS)
+
+ //
+ // Setup exported functions
+ //
+
+ memset(&fpx_functions, 0, sizeof(fpx_functions));
+
+ fpx_functions.fpx_shutdown = FPX_Shutdown; // 1
+
+#if defined(OPENSSL) || defined(GNUTLS)
+ fpx_functions.fpx_sslsocket_create = FPX_SSLSocket_Create; // 2
+ fpx_functions.fpx_sslsocket_destroy = FPX_SSLSocket_Destroy; // 3
+ fpx_functions.fpx_sslsocket_connect = FPX_SSLSocket_Connect; // 4
+ fpx_functions.fpx_sslsocket_receive = FPX_SSLSocket_Receive; // 5
+ fpx_functions.fpx_sslsocket_send = FPX_SSLSocket_Send; // 6
+#endif // defined(OPENSSL) || defined(GNUTLS)
+
+#if defined(ALSA) || defined(OSS) || defined(PULSEAUDIO)
+ fpx_functions.fpx_soundoutput_open = FPX_SoundOutput_Open; // 7
+ fpx_functions.fpx_soundoutput_close = FPX_SoundOutput_Close; // 8
+ fpx_functions.fpx_soundoutput_latency = FPX_SoundOutput_Latency; // 9
+#endif // defined(ALSA) || defined(OSS)
+
+#ifdef V4L1
+ fpx_functions.fpx_videoinput_open = FPX_VideoInput_Open; // 10
+ fpx_functions.fpx_videoinput_close = FPX_VideoInput_Close; // 11
+ fpx_functions.fpx_videoinput_getframe = FPX_VideoInput_GetFrame; // 12
+#endif // V4L1
+
+ fpx_functions.fpx_count = 14;
+
+#ifdef OPENSSL
+ SSL_library_init();
+#elif defined(GNUTLS)
+ gnutls_global_init();
+#endif // GNUTLS
+
+ return (void *)&fpx_functions;
+}
+
+static void FPX_Shutdown()
+{
+#ifdef OPENSSL
+
+#elif defined(GNUTLS)
+ gnutls_global_deinit();
+#endif // GNUTLS
+}
+
+//
+// SSL support functions
+//
+
+#ifdef OPENSSL
+struct SSL_Instance {
+ SSL *ssl;
+ SSL_CTX *sslCtx;
+};
+
+static void *FPX_SSLSocket_Create(int socket_fd)
+// return = instance pointer
+{
+ struct SSL_Instance *instance = (struct SSL_Instance *)FPI_Mem_Alloc(sizeof(struct SSL_Instance));
+ memset(instance,0,sizeof(struct SSL_Instance));
+
+ if ( ( instance->sslCtx = SSL_CTX_new( TLSv1_client_method() ) ) == 0 ) goto fail;
+
+ if ( ( instance->ssl = SSL_new(instance->sslCtx) ) == 0 ) goto fail;
+
+ if ( SSL_set_fd(instance->ssl, socket_fd) < 0 ) goto fail;
+
+ return (void *)instance;
+fail:
+ if ( instance->ssl ) {
+ SSL_shutdown(instance->ssl);
+ }
+
+ if ( instance->sslCtx ) {
+ SSL_CTX_free(instance->sslCtx);
+ }
+
+ if (FPI_Mem_Free) FPI_Mem_Free(instance);
+
+ return 0;
+}
+
+static int FPX_SSLSocket_Destroy(void *ptr)
+// ptr = instance pointer
+// return = 0 on sucess, < 0 on error
+{
+ struct SSL_Instance *instance = (struct SSL_Instance *)ptr;
+
+ if ( instance->ssl ) {
+ SSL_shutdown(instance->ssl);
+ }
+
+ if ( instance->sslCtx ) {
+ SSL_CTX_free(instance->sslCtx);
+ }
+
+ if (FPI_Mem_Free) FPI_Mem_Free(instance);
+
+ return 0;
+}
+
+static int FPX_SSLSocket_Connect(void *ptr)
+// ptr = instance pointer
+// socket_fd = BSD socket fd to be associated with SSL connection
+// return = 0 on sucess, < 0 on error
+//
+// Flash Player will use errno to obtain current status
+{
+ struct SSL_Instance *instance = (struct SSL_Instance *)ptr;
+ return SSL_connect(instance->ssl);
+}
+
+static int FPX_SSLSocket_Receive(void *ptr, char *buffer, int n_bytes)
+// ptr = instance pointer
+// buffer = raw buffer to place received data into
+// n_bytes = length of buffer in bytes
+// return = actual bytes received, < 0 on error
+//
+// Flash Player will use errno to obtain current status
+{
+ struct SSL_Instance *instance = (struct SSL_Instance *)ptr;
+ return SSL_read(instance->ssl, buffer, n_bytes);
+}
+
+static int FPX_SSLSocket_Send(void *ptr, const char *buffer, int n_bytes)
+// ptr = instance pointer
+// buffer = raw buffer to be sent
+// n_bytes = length of input buffer in bytes
+// return = actual bytes sent, < 0 on error
+//
+// Flash Player will use errno to obtain current status
+{
+ struct SSL_Instance *instance = (struct SSL_Instance *)ptr;
+ return SSL_write(instance->ssl, buffer, n_bytes);
+}
+#elif defined(GNUTLS)
+struct SSL_Instance {
+ gnutls_session_t session;
+ gnutls_anon_client_credentials_t anoncred;
+};
+
+static void *FPX_SSLSocket_Create(int socket_fd)
+// return = instance pointer
+{
+ const int kx_prio[] = { GNUTLS_KX_ANON_DH, 0 };
+
+ struct SSL_Instance *instance = (struct SSL_Instance *)FPI_Mem_Alloc(sizeof(struct SSL_Instance));
+ memset(instance,0,sizeof(struct SSL_Instance));
+
+ if ( gnutls_anon_allocate_client_credentials(&instance->anoncred) < 0 ) goto fail;
+
+ if ( gnutls_init(&instance->session, GNUTLS_CLIENT) < 0 ) goto fail;
+
+ if ( gnutls_set_default_priority(instance->session) < 0 ) goto fail;
+
+ if ( gnutls_kx_set_priority(instance->session, kx_prio) < 0 ) goto fail;
+
+ if ( gnutls_credentials_set(instance->session, GNUTLS_CRD_ANON, instance->anoncred) < 0 ) goto fail;
+
+ gnutls_transport_set_ptr(instance->session, (gnutls_transport_ptr_t)socket_fd);
+
+ return (void *)instance;
+fail:
+
+ if ( instance->session ) {
+ gnutls_deinit(instance->session);
+ }
+
+ if ( instance->anoncred ) {
+ gnutls_anon_free_client_credentials(instance->anoncred);
+ }
+
+ if (FPI_Mem_Free) FPI_Mem_Free(instance);
+
+ return 0;
+}
+
+static int FPX_SSLSocket_Destroy(void *ptr)
+// ptr = instance pointer
+// return = 0 on sucess, < 0 on error
+{
+ struct SSL_Instance *instance = (struct SSL_Instance *)FPI_Mem_Alloc(sizeof(struct SSL_Instance));
+
+ gnutls_bye(instance->session, GNUTLS_SHUT_RDWR);
+
+ gnutls_deinit(instance->session);
+
+ gnutls_anon_free_client_credentials(instance->anoncred);
+
+ if (FPI_Mem_Free) FPI_Mem_Free(instance);
+
+ return 0;
+}
+
+static int FPX_SSLSocket_Connect(void *ptr)
+// ptr = instance pointer
+// socket_fd = BSD socket fd to be associated with SSL connection
+// return = 0 on sucess, < 0 on error
+//
+// Flash Player will use errno to obtain current status
+{
+ struct SSL_Instance *instance = (struct SSL_Instance *)ptr;
+ return gnutls_handshake(instance->session);
+}
+
+static int FPX_SSLSocket_Receive(void *ptr, char *buffer, int n_bytes)
+// ptr = instance pointer
+// buffer = raw buffer to place received data into
+// n_bytes = length of buffer in bytes
+// return = actual bytes received, < 0 on error
+//
+// Flash Player will use errno to obtain current status
+{
+ struct SSL_Instance *instance = (struct SSL_Instance *)ptr;
+ return gnutls_record_recv(instance->session, buffer, n_bytes);
+}
+
+static int FPX_SSLSocket_Send(void *ptr, const char *buffer, int n_bytes)
+// ptr = instance pointer
+// buffer = raw buffer to be sent
+// n_bytes = length of input buffer in bytes
+// return = actual bytes sent, < 0 on error
+//
+// Flash Player will use errno to obtain current status
+{
+ struct SSL_Instance *instance = (struct SSL_Instance *)ptr;
+ return gnutls_record_send(instance->session, buffer, n_bytes);
+}
+#endif // GNUTLS
+
+//
+// Sound support functions
+//
+#ifdef ALSA
+struct SoundOutput_Instance {
+ snd_pcm_t * handle;
+ snd_async_handler_t * async_handler;
+ sem_t semaphore;
+ pthread_t thread;
+ char * buffer;
+ snd_pcm_sframes_t buffer_size;
+ snd_pcm_sframes_t period_size;
+ snd_pcm_sframes_t buffer_pos;
+ char * buffer_ptr;
+};
+
+static void *alsa_thread(void *ptr)
+{
+ struct SoundOutput_Instance *instance = (struct SoundOutput_Instance *)ptr;
+ snd_pcm_sframes_t avail = 0;
+ int result = 0;
+ int err = 0;
+ int state = 0;
+
+ for( ; ; ) {
+
+ err = sem_wait(&instance->semaphore);
+ if ( !instance->handle ) {
+ pthread_exit(0);
+ return 0;
+ }
+
+ if ( err < 0 ) {
+ usleep(1);
+ continue;
+ }
+
+ do {
+ if ( instance->buffer_pos <= 0 ) {
+ FPI_SoundOutput_FillBuffer(ptr, instance->buffer, snd_pcm_frames_to_bytes(instance->handle, instance->period_size));
+ instance->buffer_pos = instance->period_size;
+ instance->buffer_ptr = instance->buffer;
+ }
+ do {
+ state = snd_pcm_state(instance->handle);
+ if(state != SND_PCM_STATE_RUNNING && state != SND_PCM_STATE_PREPARED) {
+ snd_pcm_prepare(instance->handle);
+ }
+ result = snd_pcm_writei(instance->handle, instance->buffer_ptr, instance->buffer_pos);
+ if( result <= 0 ) {
+ switch( result ) {
+ case -EPIPE: {
+ snd_pcm_prepare(instance->handle);
+ } break;
+ case -ESTRPIPE: {
+ err = snd_pcm_resume(instance->handle);
+ if ( err < 0 ) {
+ snd_pcm_prepare(instance->handle);
+ }
+ } break;
+ }
+ break;
+ } else {
+ instance->buffer_pos -= result;
+ instance->buffer_ptr += snd_pcm_frames_to_bytes(instance->handle, result);
+ }
+ } while (instance->buffer_pos);
+ avail = snd_pcm_avail_update(instance->handle);
+ if( avail < 0 ) {
+ switch( avail ) {
+ case -EPIPE: {
+ snd_pcm_prepare(instance->handle);
+ } break;
+ case -ESTRPIPE: {
+ err = snd_pcm_resume(instance->handle);
+ if ( err < 0 ) {
+ snd_pcm_prepare(instance->handle);
+ }
+ } break;
+ }
+ break;
+ }
+
+ } while(avail >= instance->period_size);
+ }
+}
+
+static void alsa_callback(snd_async_handler_t *ahandler)
+{
+ struct SoundOutput_Instance *instance = (struct SoundOutput_Instance *)snd_async_handler_get_callback_private(ahandler);
+ // signal mixer thread
+ sem_post(&instance->semaphore);
+}
+
+static void *FPX_SoundOutput_Open()
+// return = instance pointer
+{
+ struct SoundOutput_Instance *instance = 0;
+ snd_pcm_hw_params_t *hwparams = 0;
+ snd_pcm_sw_params_t *swparams = 0;
+ snd_pcm_format_t pcm_format;
+ unsigned int buffer_time = 500000;
+ unsigned int period_time = 20000;
+ unsigned int actual_rate;
+ snd_pcm_uframes_t size;
+ int direction;
+ void *retVal = 0;
+ int err = 0;
+
+ if ( !FPI_SoundOutput_FillBuffer ) goto fail;
+ if ( !FPI_Mem_Alloc ) goto fail;
+
+ instance = FPI_Mem_Alloc(sizeof(struct SoundOutput_Instance));
+ memset(instance,0,sizeof(struct SoundOutput_Instance));
+
+ if ( ( err = snd_pcm_open(&instance->handle, "default", SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK) ) < 0) {
+ if ( ( err = snd_pcm_open(&instance->handle, "plughw:0,0", SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK) ) < 0) {
+ goto fail;
+ }
+ }
+
+ snd_pcm_hw_params_alloca(&hwparams);
+ snd_pcm_sw_params_alloca(&swparams);
+
+ if (snd_pcm_hw_params_any(instance->handle, hwparams) < 0) goto fail;
+
+ if (snd_pcm_hw_params_set_access(instance->handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) goto fail;
+
+ pcm_format = SND_PCM_FORMAT_S16_LE;
+
+ if (snd_pcm_hw_params_set_format(instance->handle, hwparams, pcm_format) < 0) goto fail;
+
+ if (snd_pcm_hw_params_set_channels(instance->handle, hwparams, 2) < 0) goto fail;
+
+ actual_rate = 44100;
+
+ if (snd_pcm_hw_params_set_rate_near(instance->handle, hwparams, &actual_rate, 0) < 0) goto fail;
+
+ if (actual_rate != 44100) goto fail;
+
+ if (snd_pcm_hw_params_set_buffer_time_near(instance->handle, hwparams, &buffer_time, &direction) < 0) goto fail;
+
+ if (snd_pcm_hw_params_get_buffer_size(hwparams, &size) < 0) goto fail;
+
+ instance->buffer_size = (snd_pcm_sframes_t)size;
+
+ if (snd_pcm_hw_params_set_period_time_near(instance->handle, hwparams, &period_time, &direction) < 0) goto fail;
+
+ if (snd_pcm_hw_params_get_period_size(hwparams, &size, &direction) < 0) goto fail;
+
+ instance->period_size = (snd_pcm_sframes_t)size;
+
+ if (snd_pcm_hw_params(instance->handle, hwparams) < 0) goto fail;
+
+ if (snd_pcm_sw_params_current(instance->handle, swparams) < 0) goto fail;
+
+ if (snd_pcm_sw_params_set_start_threshold(instance->handle, swparams, ((instance->buffer_size-1) / instance->period_size) * instance->period_size) < 0) goto fail;
+
+ if (snd_pcm_sw_params_set_stop_threshold(instance->handle, swparams, ~0U) < 0) goto fail;
+
+ if (snd_pcm_sw_params_set_avail_min(instance->handle, swparams, instance->period_size) < 0) goto fail;
+
+ if (snd_pcm_sw_params_set_xfer_align(instance->handle, swparams, 1) < 0) goto fail;
+
+ if (snd_pcm_sw_params(instance->handle, swparams) < 0) goto fail;
+
+ if (snd_async_add_pcm_handler(&instance->async_handler, instance->handle, &alsa_callback, instance) < 0) goto fail;
+
+ if ( ( instance->buffer = FPI_Mem_Alloc(instance->buffer_size * 2 * 2 * 2) ) == 0 ) goto fail;
+
+ if ( pthread_create(&instance->thread, 0, alsa_thread, instance) < 0 ) goto fail;
+
+ sem_post(&instance->semaphore);
+
+ return instance;
+
+fail:
+ if ( instance ) {
+ if ( instance->handle ) {
+ snd_pcm_drop(instance->handle);
+ snd_pcm_close(instance->handle);
+ instance->handle = 0;
+ }
+ if ( instance->thread ) {
+ sem_post(&instance->semaphore);
+ sem_destroy(&instance->semaphore);
+ pthread_join(instance->thread,&retVal);
+ }
+ if ( instance->buffer ) {
+ if ( FPI_Mem_Free ) FPI_Mem_Free(instance->buffer);
+ }
+ if ( FPI_Mem_Free ) FPI_Mem_Free(instance);
+ }
+ return 0;
+}
+
+static int FPX_SoundOutput_Close(void *ptr)
+// ptr = instance pointer
+// return = 0 on success, < 0 on error
+{
+ struct SoundOutput_Instance *instance = (struct SoundOutput_Instance *)ptr;
+ void *retVal = 0;
+ if ( instance->handle ) {
+ snd_pcm_drop(instance->handle);
+ snd_pcm_close(instance->handle);
+ instance->handle = 0;
+ }
+ if ( instance->thread ) {
+ sem_post(&instance->semaphore);
+ sem_destroy(&instance->semaphore);
+ pthread_join(instance->thread,&retVal);
+ }
+ if ( instance->buffer ) {
+ if ( FPI_Mem_Free ) FPI_Mem_Free(instance->buffer);
+ }
+ if ( FPI_Mem_Free ) FPI_Mem_Free(instance);
+ return 0;
+}
+
+static int FPX_SoundOutput_Latency(void *ptr)
+// ptr = instance pointer
+// return = 0 on success, < 0 on error
+{
+ struct SoundOutput_Instance *instance = (struct SoundOutput_Instance *)ptr;
+ if ( instance->handle ) {
+ snd_pcm_sframes_t delay = 0;
+ snd_pcm_delay(instance->handle, &delay);
+ if ( snd_pcm_state(instance->handle) == SND_PCM_STATE_RUNNING && delay > 0 ) {
+ return delay;
+ } else {
+ return 0;
+ }
+ }
+ return -1;
+}
+#elif defined(OSS)
+struct SoundOutput_Instance {
+ int oss_fd;
+ pthread_t thread;
+ int signal;
+};
+
+static void *oss_thread(void *ptr)
+{
+ struct SoundOutput_Instance *instance = (struct SoundOutput_Instance *)ptr;
+ char buffer[4096];
+ int len = 0;
+ int written = 0;
+ for(;;) {
+ FPI_SoundOutput_FillBuffer(ptr,buffer,4096);
+ len = 4096;
+ while ( len ) {
+ written = write(instance->oss_fd, buffer, len);
+ if ( written >= 0 ) {
+ len -= written;
+ }
+ if ( instance->signal ) {
+ pthread_exit(0);
+ }
+ if ( written < 0 ) {
+ usleep(100);
+ }
+ }
+ }
+}
+
+static void *FPX_SoundOutput_Open()
+// return = instance pointer
+{
+ struct SoundOutput_Instance *instance = 0;
+ int format = AFMT_S16_LE;
+ int stereo = 1;
+ int speed = 44100;
+
+ if ( !FPI_SoundOutput_FillBuffer ) goto fail;
+ if ( !FPI_Mem_Alloc ) goto fail;
+
+ instance = (struct SoundOutput_Instance *)FPI_Mem_Alloc(sizeof(struct SoundOutput_Instance));
+ memset(instance,0,sizeof(struct SoundOutput_Instance));
+
+ if ( ( instance->oss_fd = open("/dev/dsp",O_WRONLY) ) < 0 ) goto fail;
+
+ if ( ioctl(instance->oss_fd, SNDCTL_DSP_SETFMT, &format) < 0 ) goto fail;
+
+ if ( ioctl(instance->oss_fd, SNDCTL_DSP_STEREO, &stereo) < 0 ) goto fail;
+
+ if ( ioctl(instance->oss_fd, SNDCTL_DSP_SPEED, &speed) < 0 ) goto fail;
+
+ if ( pthread_create(&instance->thread, 0, oss_thread, instance) < 0 ) goto fail;
+
+ return instance;
+fail:
+ if ( instance ) {
+ if ( FPI_Mem_Free ) FPI_Mem_Free(instance);
+ }
+ return 0;
+}
+
+static int FPX_SoundOutput_Close(void *ptr)
+// ptr = instance pointer
+// return = 0 on success, < 0 on error
+{
+ struct SoundOutput_Instance *instance = (struct SoundOutput_Instance *)ptr;
+ void *retVal = 0;
+
+ instance->signal = 1;
+
+ if ( instance->oss_fd ) {
+ ioctl(instance->oss_fd, SNDCTL_DSP_RESET, 0);
+ }
+
+ if ( instance->thread ) {
+ pthread_join(instance->thread,&retVal);
+ }
+
+ if ( instance->oss_fd ) {
+ close(instance->oss_fd);
+ }
+
+ if ( FPI_Mem_Free ) FPI_Mem_Free(instance);
+
+ return 0;
+}
+
+static int FPX_SoundOutput_Latency(void *ptr)
+// ptr = instance pointer
+// return = 0 on success, < 0 on error
+{
+ struct SoundOutput_Instance *instance = (struct SoundOutput_Instance *)ptr;
+ if ( instance->oss_fd ) {
+ int value = 0;
+ if ( ( value = ioctl(instance->oss_fd,SNDCTL_DSP_GETODELAY,&value) ) == 0 ) {
+ return value / 4;
+ }
+ return 0;
+ }
+ return -1;
+}
+#endif // defined(OSS)
+
+#ifdef PULSEAUDIO
+
+#define BUFSIZE (4096)
+
+struct output_data {
+ pa_threaded_mainloop *mainloop;
+ pa_context *context;
+ pa_stream *stream;
+ uint8_t buf[BUFSIZE];
+ pthread_t thread_id;
+ int first;
+};
+
+static void context_state_cb(pa_context *c, void *userdata) {
+ struct output_data *p = userdata;
+
+ assert(c);
+ assert(p);
+
+ p->thread_id = pthread_self();
+ p->context = c;
+
+ switch (pa_context_get_state(c)) {
+ case PA_CONTEXT_READY:
+ case PA_CONTEXT_TERMINATED:
+ case PA_CONTEXT_FAILED:
+ pa_threaded_mainloop_signal(p->mainloop, 0);
+ break;
+
+ case PA_CONTEXT_UNCONNECTED:
+ case PA_CONTEXT_CONNECTING:
+ case PA_CONTEXT_AUTHORIZING:
+ case PA_CONTEXT_SETTING_NAME:
+ break;
+ }
+}
+
+static void stream_state_cb(pa_stream *s, void *userdata) {
+ struct output_data *p = userdata;
+
+ assert(s);
+ assert(p);
+
+ p->thread_id = pthread_self();
+ p->stream = s;
+
+ switch (pa_stream_get_state(s)) {
+
+ case PA_STREAM_READY:
+ case PA_STREAM_FAILED:
+ case PA_STREAM_TERMINATED:
+ pa_threaded_mainloop_signal(p->mainloop, 0);
+ break;
+
+ case PA_STREAM_UNCONNECTED:
+ case PA_STREAM_CREATING:
+ break;
+ }
+}
+
+static void write_data(struct output_data *p) {
+ size_t length;
+
+ assert(p);
+
+ /* Wait until timing info is available before we write the second
+ * and all subsequent blocks */
+ if (!p->first && !pa_stream_get_timing_info(p->stream))
+ return;
+
+ length = pa_stream_writable_size(p->stream);
+
+ while (length > 0) {
+ size_t l = length;
+
+ if (l > BUFSIZE)
+ l = BUFSIZE;
+
+ FPI_SoundOutput_FillBuffer(p, (char*) p->buf, l);
+
+ if (pa_stream_write(p->stream, p->buf, l, NULL, 0, PA_SEEK_RELATIVE) < 0)
+ break;
+
+ length -= l;
+
+ if (p->first)
+ break;
+ }
+
+ /* The handling of errors here is just ridicilous. Blame Adobe! */
+
+ p->first = 0; /* So, we write the first block noch, remember that */
+}
+
+static void stream_request_cb(pa_stream *s, size_t length, void *userdata) {
+ struct output_data *p = userdata;
+
+ assert(s);
+ assert(length > 0);
+ assert(p);
+
+ p->thread_id = pthread_self();
+
+ /* Write some data */
+ write_data(p);
+}
+
+static void stream_latency_update_cb(pa_stream *s, void *userdata) {
+ struct output_data *p = userdata;
+
+ assert(s);
+ assert(p);
+
+ p->thread_id = pthread_self();
+
+ /* Try to write some more, in case we delayed the first write until latency data became available */
+ write_data(p);
+}
+
+static void *FPX_SoundOutput_Open(void) {
+
+ static const pa_sample_spec ss = {
+ .format = PA_SAMPLE_S16LE, /* Hmm, Flash wants LE here, not NE. I hope they know what they are doing. Probably not. */
+ .rate = 44100,
+ .channels = 2
+ };
+
+ struct output_data *p = NULL;
+
+ /* Awesome, we don't inform the user about any error messages, I
+ * guess that's Adobe style programming. Rock!" */
+
+ if (!(p = FPI_Mem_Alloc(sizeof(struct output_data))))
+ goto fail;
+
+ memset(p, 0, sizeof(*p));
+ p->first = 1;
+ p->thread_id = (pthread_t) 0;
+
+ /* First, let's create the main loop */
+ if (!(p->mainloop = pa_threaded_mainloop_new()))
+ goto fail;
+
+ /* Second, initialize the connection context */
+ if (!(p->context = pa_context_new(pa_threaded_mainloop_get_api(p->mainloop), "Adobe Flash")))
+ goto fail;
+
+ pa_context_set_state_callback(p->context, context_state_cb, p);
+
+ if (pa_context_connect(p->context, NULL, 0, NULL) < 0)
+ goto fail;
+
+ /* Now, let's start the background thread */
+ pa_threaded_mainloop_lock(p->mainloop);
+
+ if (pa_threaded_mainloop_start(p->mainloop) < 0)
+ goto fail;
+
+ /* Wait until the context is ready */
+ pa_threaded_mainloop_wait(p->mainloop);
+
+ if (pa_context_get_state(p->context) != PA_CONTEXT_READY)
+ goto unlock_and_fail;
+
+ /* Now, initialize the stream */
+ if (!(p->stream = pa_stream_new(p->context, "Flash Animation", &ss, NULL)))
+ goto unlock_and_fail;
+
+ pa_stream_set_state_callback(p->stream, stream_state_cb, p);
+ pa_stream_set_write_callback(p->stream, stream_request_cb, p);
+ pa_stream_set_latency_update_callback(p->stream, stream_latency_update_cb, p);
+
+ if (pa_stream_connect_playback(p->stream, NULL, NULL, PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE, NULL, NULL) < 0)
+ goto unlock_and_fail;
+
+ /* Wait until the stream is ready */
+ pa_threaded_mainloop_wait(p->mainloop);
+
+ if (pa_stream_get_state(p->stream) != PA_STREAM_READY)
+ goto unlock_and_fail;
+
+ pa_threaded_mainloop_unlock(p->mainloop);
+
+ return p;
+
+unlock_and_fail:
+
+ pa_threaded_mainloop_unlock(p->mainloop);
+
+fail:
+ if (p)
+ FPX_SoundOutput_Close(p);
+
+ return NULL;
+}
+
+static int FPX_SoundOutput_Close(void *ptr) {
+ struct output_data *p = ptr;
+
+ assert(p);
+
+ if (p->mainloop)
+ pa_threaded_mainloop_stop(p->mainloop);
+
+ if (p->stream)
+ pa_stream_unref(p->stream);
+
+ if (p->context)
+ pa_context_unref(p->context);
+
+ if (p->mainloop)
+ pa_threaded_mainloop_free(p->mainloop);
+
+ if (FPI_Mem_Free)
+ FPI_Mem_Free(p);
+
+ return 0;
+}
+
+static int FPX_SoundOutput_Latency(void *ptr) {
+ struct output_data *p = ptr;
+ pa_usec_t t;
+ int negative;
+ int r;
+
+ assert(p);
+
+ /* We lock here only if we are not called from our event loop thread */
+ if (!p->thread_id || !pthread_equal(p->thread_id, pthread_self()))
+ pa_threaded_mainloop_lock(p->mainloop);
+
+ if (pa_stream_get_latency(p->stream, &t, &negative) < 0 || negative)
+ r = 0;
+ else
+ r = (int) (pa_usec_to_bytes(t, pa_stream_get_sample_spec(p->stream)) >> 2);
+
+ if (!p->thread_id || !pthread_equal(p->thread_id, pthread_self()))
+ pa_threaded_mainloop_unlock(p->mainloop);
+
+ return r;
+}
+
+#endif
+
+#ifdef V4L1
+struct VideoOutput_Instance {
+ int v4l_fd;
+ pthread_t thread;
+ int signal;
+ char * buffer[2];
+ int buffercurrent;
+ int buffersize;
+ struct video_window window;
+ struct video_picture picture;
+};
+
+static void *v4l_thread(void *ptr)
+{
+ struct VideoOutput_Instance *instance = (struct VideoOutput_Instance *)ptr;
+ int result;
+ int status;
+
+ for(;;) {
+
+ result = read(instance->v4l_fd, instance->buffer[instance->buffercurrent], instance->buffersize);
+ if(result > 0) {
+ }
+
+ if ( result < 0 ) {
+ usleep(10000);
+ }
+
+ if ( instance->signal ) {
+ status = 0;
+ ioctl(instance->v4l_fd, VIDIOCCAPTURE, &status);
+ pthread_exit(0);
+ }
+ }
+}
+
+static void *FPX_VideoInput_Open()
+{
+ struct VideoOutput_Instance *instance = 0;
+
+ if ( !FPI_Mem_Alloc ) goto fail;
+
+ instance = (struct VideoOutput_Instance *)FPI_Mem_Alloc(sizeof(struct VideoOutput_Instance));
+ memset(instance,0,sizeof(struct VideoOutput_Instance));
+
+ if ( ( instance->v4l_fd = open("/dev/video", O_RDONLY) ) < 0 ) goto fail;
+
+ if ( ioctl(instance->v4l_fd, VIDIOCGPICT, &instance->picture) < 0 ) goto fail;
+
+ switch(instance->picture.palette) {
+ case VIDEO_PALETTE_YUV420P:
+ break;
+ case VIDEO_PALETTE_RGB24:
+ case VIDEO_PALETTE_YUV422P:
+ case VIDEO_PALETTE_YUV411P:
+ case VIDEO_PALETTE_YUV410P:
+ case VIDEO_PALETTE_GREY:
+ case VIDEO_PALETTE_HI240:
+ case VIDEO_PALETTE_RGB565:
+ case VIDEO_PALETTE_RGB32:
+ case VIDEO_PALETTE_RGB555:
+ case VIDEO_PALETTE_YUV422:
+ case VIDEO_PALETTE_YUYV:
+ case VIDEO_PALETTE_UYVY:
+ case VIDEO_PALETTE_YUV420:
+ case VIDEO_PALETTE_YUV411:
+ case VIDEO_PALETTE_RAW:
+ default:
+ goto fail;
+ }
+
+ if( ioctl(instance->v4l_fd, VIDIOCGWIN, &instance->window) < 0 ) goto fail;
+
+ instance->buffer[0] = FPI_Mem_Alloc(instance->window.width * instance->window.height * 2);
+ instance->buffer[1] = FPI_Mem_Alloc(instance->window.width * instance->window.height * 2);
+
+ if ( pthread_create(&instance->thread, 0, v4l_thread, instance) < 0 ) goto fail;
+
+ return instance;
+
+fail:
+ if ( FPI_Mem_Free ) {
+ if ( instance->buffer[0] ) {
+ FPI_Mem_Free(instance->buffer[0]);
+ }
+ if ( instance->buffer[1] ) {
+ FPI_Mem_Free(instance->buffer[1]);
+ }
+ FPI_Mem_Free(instance);
+ }
+ return 0;
+}
+
+static int FPX_VideoInput_Close(void *ptr)
+{
+ struct VideoOutput_Instance *instance = (struct VideoOutput_Instance *)ptr;
+ void *retVal = 0;
+
+ instance->signal = 1;
+
+ if ( instance->thread ) {
+ pthread_join(instance->thread,&retVal);
+ }
+
+ if ( instance->v4l_fd ) {
+ close(instance->v4l_fd);
+ }
+
+ if ( FPI_Mem_Free ) {
+ if ( instance->buffer[0] ) {
+ FPI_Mem_Free(instance->buffer[0]);
+ }
+ if ( instance->buffer[1] ) {
+ FPI_Mem_Free(instance->buffer[1]);
+ }
+ FPI_Mem_Free(instance);
+ }
+
+ return 0;
+}
+
+static int FPX_VideoInput_GetFrame(void *ptr, char *data, int width, int height, int pitch_n_bytes)
+{
+ struct VideoOutput_Instance *instance = (struct VideoOutput_Instance *)ptr;
+ int ix, iy, ox, oy, ow, oh, dx, dy, Y, U, V, R, G, B;
+ unsigned char *y, *u, *v;
+
+ switch(instance->picture.palette) {
+ case VIDEO_PALETTE_YUV420P: {
+ ow = instance->window.width;
+ oh = instance->window.height;
+
+ dx = (ow<<16) / width;
+ dy = (oh<<16) / height;
+
+ y = (unsigned char *)instance->buffer[instance->buffercurrent^1];
+ u = y + ow * oh;
+ v = u + ow * oh / 4;
+
+ oy = 0;
+
+ for ( iy = 0; iy < height; iy++ ) {
+
+ ox = 0;
+
+ for ( ix = 0; ix < width; ix++ ) {
+
+ Y = ( 149 * ((int)(y[(oy>>16)*(ow )+(ox>>16)]) - 16) ) / 2;
+ U = (int)(u[(oy>>17)*(ow/2)+(ox>>17)]) - 128;
+ V = (int)(v[(oy>>17)*(ow/2)+(ox>>17)]) - 128;
+
+ R = (Y + V * 102 ) / 64;
+ G = (Y - V * 52 - U * 25 ) / 64;
+ B = (Y + U * 129 ) / 64;
+
+ R = R < 0 ? 0 : ( R > 255 ? 255 : R );
+ G = G < 0 ? 0 : ( G > 255 ? 255 : G );
+ B = B < 0 ? 0 : ( B > 255 ? 255 : B );
+
+ data[ix*3+0] = R;
+ data[ix*3+1] = G;
+ data[ix*3+2] = B;
+
+ ox += dx;
+ }
+
+ oy += dy;
+
+ data += pitch_n_bytes;
+ }
+ } break;
+ default:
+ goto fail;
+ }
+
+ instance->buffercurrent ^= 1;
+
+ return 0;
+
+fail:
+ return -1;
+}
+#endif // V4L1