/*
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.

The Sound detection routines, ESD and PulseAudio backends have been
writen by Jean-Michel Dault <jmdault@revolutionlinux.com and are
Copyright (c) 2006 Revolution Linux inc. All rights reserved.
The code is licenced with the same terms as the rest of the Adobe code.


*/
////////////////////////////////////////////////////////////////////////////////////////////////////
//
// File name: flashsupport.c
// Targets: Adobe Flash Player 9.1 beta 1 for Linux
// Last Revision Date: 10/26/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 ICU

#define PULSEAUDIO
#define ESD
//#define ALSA
#define ALSA_INTERNAL
#define OSS

/* To make auto-detection use Flash's internal ALSA functions
   instead of libflashsupport external ALSA functions
*/
#define ALSA_INTERNAL

//#define GNUTLS
//#define V4L1

////////////////////////////////////////////////////////////////////////////////////////////////////
//
// 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)
//  - ICU developer package and working user libraries (ICU 3.4.1 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 -licuuc -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(ESD) || 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) || defined(ESD) || defined(PULSEAUDIO)

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)

#ifdef ICU
static char *FPX_Unicode_CodePageToUTF8(const char *buffer, int code_page);
static char *FPX_Unicode_UTF8ToCodePage(const char *buffer, int code_page);
#endif // ICU

#if defined(ALSA)
static void *ALSA_FPX_SoundOutput_Open(void);
static int ALSA_FPX_SoundOutput_Close(void *ptr);
static int ALSA_FPX_SoundOutput_Latency(void *ptr);
#endif // defined(ALSA)

#if defined(OSS)
static void *OSS_FPX_SoundOutput_Open(void);
static int OSS_FPX_SoundOutput_Close(void *ptr);
static int OSS_FPX_SoundOutput_Latency(void *ptr);
#endif // defined(OSS)

#if defined(ESD)
static void *ESD_FPX_SoundOutput_Open(void);
static int ESD_FPX_SoundOutput_Close(void *ptr);
static int ESD_FPX_SoundOutput_Latency(void *ptr);
#endif // defined(ESD)

#if defined(PULSEAUDIO)
static void *PULSEAUDIO_FPX_SoundOutput_Open(void);
static int PULSEAUDIO_FPX_SoundOutput_Close(void *ptr);
static int PULSEAUDIO_FPX_SoundOutput_Latency(void *ptr);
#endif // defined(PULSEAUDIO)

#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_unicode_codepagetoutf8;	// 7
	void *fpx_unicode_utf8tocodepage;	// 8
	void *fpx_soundoutput_open;			// 9
	void *fpx_soundoutput_close;		// 10
	void *fpx_soundoutput_latency;		// 11
	void *fpx_videoinput_open;			// 12
	void *fpx_videoinput_close;			// 13
	void *fpx_videoinput_getframe;		// 14
};

#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 ICU
#include <stdio.h>
#include <unicode/ucnv.h>
#endif // ICU

#ifdef ALSA
#include <pthread.h>
#include <semaphore.h>
#include <alsa/asoundlib.h>
#endif

#if defined(OSS)
#include <unistd.h>
#include <pthread.h>
#include <linux/soundcard.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#endif

#if defined(ESD)
#include <unistd.h>
#include <esd.h>
#include <pthread.h>
#include <signal.h>
#include <dlfcn.h>
#endif

#if defined(PULSEAUDIO)
#include <pulse/simple.h>
#include <pulse/error.h>
#include <unistd.h>
#include <pthread.h>
#endif

#ifdef V4L1
#include <unistd.h>
#include <pthread.h>
#include <linux/videodev.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#endif // V4L1

//Includes for output driver detection
#if defined(ALSA) || defined(OSS) || defined(ESD) || defined(PULSEAUDIO)
#include <stdlib.h> //getenv
#include <sys/types.h> //stat
#include <sys/stat.h> //stat
#include <unistd.h> //stat
#endif // defined(ALSA) || defined(OSS) || defined(ESD) || defined(PULSEAUDIO)

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(ESD) || defined(PULSEAUDIO)
static T_FPI_SoundOutput_FillBuffer FPI_SoundOutput_FillBuffer = 0;
#endif // defined(ALSA) || defined(OSS) || defined(ESD) || defined(PULSEAUDIO)

#if defined(ALSA) || defined(OSS) || defined(ESD) || defined(PULSEAUDIO)
/* JMD: Choose between multiple audio interfaces by order of priority:
- Pulse is always first, since it can support esound, gstreamer, alsa, oss
  and jack and is easily identified by environment variables or socket.
  Supports full audio/video synchronization, even over the network.
- Esound is second, since it's by default in many distributions, and supports
  oss. NOTE: audio and video are not synchronized, since the latency
  functions can hang the browser.
- Arts (TODO) is next. It's not currently supported because it's being
  phased out. Besides, it can use Esound as a backend
- NASD: Not supported for the moment, there is not much information
  available...
- Jack (TODO). When it's done, it should go after Pulse, but before ALSA.
- ALSA: default if it's supported
- Finally, if nothing else, fallback on oss
*/

#define AUDIO_PULSE 128
#define AUDIO_ESD 64
#define AUDIO_ALSA 2
#define AUDIO_OSS 1

//ESD functions
int (*FPX_esd_play_stream_fallback)( esd_format_t format, int rate,
	const char *host, const char *name );

//PULSE functions
pa_simple* (*FPX_pa_simple_new) (const char *server,const char *name,
    pa_stream_direction_t dir,const char *dev,const char *stream_name,
    const pa_sample_spec *ss,const pa_channel_map *map,
    const pa_buffer_attr *attr,int *error);

void (*FPX_pa_simple_free) (pa_simple *s);

int (*FPX_pa_simple_write) (pa_simple *s, const void*data, size_t length, int *error);

int (*FPX_pa_simple_drain) (pa_simple *s, int *error);

int (*FPX_pa_simple_read) (pa_simple *s, void*data, size_t length, int *error);

pa_usec_t (*FPX_pa_simple_get_latency) (pa_simple *s, int *error);

int (*FPX_pa_simple_flush) (pa_simple *s, int *error);

const char* (*FPX_pa_strerror) (int error);

                     
static int audiotype=2;
static int audiodrivers=0;
static int audiodebug=0;
static int pulsedebug=0;

void FPX_SoundOutput_Detect()
{
	char tmpstr[1024]="";
	char *tmpenv;
	struct stat buf;
	void *handle;
	char *error;	

	if((tmpenv=getenv("FLASH_AUDIODEBUG"))!=NULL) {
		audiodebug=1;
	}
	if((tmpenv=getenv("FLASH_PULSEDEBUG"))!=NULL) {
		pulsedebug=1;
	}

	if(audiodebug) {
		fprintf( stderr,"Flash sound output detection routine.\n\
		(c) 2006 Revolution Linux inc, \
		Jean-Michel Dault <jmdault@revolutionlinux.com>\n");
	}

#if defined(PULSEAUDIO)
	//Check if PULSE AUDIO is running
	if((tmpenv=getenv("USER"))!=NULL) {
		strcpy(tmpstr,"/tmp/pulse-");
		strcat(tmpstr,tmpenv);
		strcat(tmpstr,"/native");
		if(!stat(tmpstr,&buf)) {
			if(audiodebug) fprintf( stderr, "PulseAudio socket found\n");
			audiodrivers = audiodrivers | AUDIO_PULSE;
		}
	}
	//Check if PULSE AUDIO is running system-wide
	strcpy(tmpstr,"/var/lib/run/pulse/native");
	if(!stat(tmpstr,&buf)) {
		if(audiodebug) fprintf( stderr, "PulseAudio system-wide found\n");
		audiodrivers = audiodrivers | AUDIO_PULSE;
	}
	//Pulse over network
	if((tmpenv=getenv("PULSE_SERVER"))!=NULL) {
		if(audiodebug) fprintf( stderr, "PulseAudio SERVER variable present\n");
		audiodrivers = audiodrivers | AUDIO_PULSE;
	}
	//Pulse autospawn daemon
	if((tmpenv=getenv("PULSE_BINARY"))!=NULL) {
		if(audiodebug) fprintf( stderr, "PulseAudio BINARY variable present\n");
		audiodrivers = audiodrivers | AUDIO_PULSE;
	}
#endif
#if defined(ESD)
	//Check if ESD is running
	if(!stat("/tmp/.esd/socket",&buf)) {
		if(audiodebug) fprintf( stderr, "ESD socket found\n");
		audiodrivers = audiodrivers | AUDIO_ESD;
	}
	//ESD over network
	if((tmpenv=getenv("ESPEAKER"))!=NULL) {
		if(audiodebug) fprintf( stderr, "ESD variable ESPEAKER found\n");
		audiodrivers = audiodrivers | AUDIO_ESD;
	}

#endif
#if defined(ALSA) || defined(ALSA_INTERNAL)
	//Check for ALSA device
	if(!stat("/proc/asound",&buf)) {
		if(audiodebug) fprintf( stderr, "ALSA device detected\n");
		audiodrivers = audiodrivers | AUDIO_ALSA;
	}
#endif
#if defined(OSS)
	//Check for OSS device
	if(!stat("/dev/dsp",&buf)) {
		if(audiodebug) fprintf( stderr, "OSS device present\n");
		audiodrivers = audiodrivers | AUDIO_OSS;
	}
#endif

	if((tmpenv=getenv("FLASH_FORCE_PULSEAUDIO"))!=NULL) {
#if defined(PULSEAUDIO)
			if(audiodebug) fprintf( stderr, "Forcing PulseAudio\n");
			audiodrivers = AUDIO_PULSE;
#else
			if(audiodebug) fprintf( stderr, "PulseAudio unavailable: please recompile libflashsupport.so!\n");			
#endif
	}

	if((tmpenv=getenv("FLASH_FORCE_ESD"))!=NULL) {
#if defined(ESD)
			if(audiodebug) fprintf( stderr, "Forcing ESD\n");			
			audiodrivers = AUDIO_ESD;
#else
			if(audiodebug) fprintf( stderr, "ESD unavailable: please recompile libflashsupport.so!\n");			
#endif
	}

	if((tmpenv=getenv("FLASH_FORCE_ALSA"))!=NULL) {
#if defined(ALSA) || defined(ALSA_INTERNAL)
			if(audiodebug) fprintf( stderr, "Forcing ALSA\n");			
			audiodrivers = AUDIO_ALSA;
#else
			if(audiodebug) fprintf( stderr, "ALSA unavailable: please recompile libflashsupport.so!\n");			
#endif
	}

	if((tmpenv=getenv("FLASH_FORCE_OSS"))!=NULL) {
#if defined(OSS)
			if(audiodebug) fprintf( stderr, "Forcing OSS\n");			
			audiodrivers = AUDIO_OSS;
#else
			if(audiodebug) fprintf( stderr, "OSS unavailable: please recompile libflashsupport.so!\n");			
#endif
	}

//Check for required libraries and functions
#if defined(PULSEAUDIO)
	if((audiodrivers & AUDIO_PULSE) && !FPX_pa_simple_new) {
		handle = dlopen("/usr/lib/libpulse-simple.so.0", RTLD_LAZY);
		if (!handle) {
			if(audiodebug) fprintf(stderr,"Can't load /usr/lib/libpulse-simple.so.0: %s\n",dlerror());
			audiodrivers = audiodrivers & (~AUDIO_PULSE);
		}
		FPX_pa_simple_new = dlsym(handle, "pa_simple_new");
		if ((error = dlerror()) != NULL) {
			if(audiodebug) fprintf(stderr, "%s\n", error);
			audiodrivers = audiodrivers & (~AUDIO_PULSE);
		}
		FPX_pa_simple_write = dlsym(handle, "pa_simple_write");
		if ((error = dlerror()) != NULL) {
			if(audiodebug) fprintf(stderr, "%s\n", error);
			audiodrivers = audiodrivers & (~AUDIO_PULSE);
		}
		FPX_pa_simple_drain = dlsym(handle, "pa_simple_drain");
		if ((error = dlerror()) != NULL) {
			if(audiodebug) fprintf(stderr, "%s\n", error);
			audiodrivers = audiodrivers & (~AUDIO_PULSE);
		}
		FPX_pa_simple_free = dlsym(handle, "pa_simple_free");
		if ((error = dlerror()) != NULL) {
			if(audiodebug) fprintf(stderr, "%s\n", error);
			audiodrivers = audiodrivers & (~AUDIO_PULSE);
		}
		FPX_pa_simple_get_latency = dlsym(handle, "pa_simple_get_latency");
		if ((error = dlerror()) != NULL) {
			if(audiodebug) fprintf(stderr, "%s\n", error);
			audiodrivers = audiodrivers & (~AUDIO_PULSE);
		}
		FPX_pa_strerror = dlsym(handle, "pa_strerror");
		if ((error = dlerror()) != NULL) {
			if(audiodebug) fprintf(stderr, "%s\n", error);
			audiodrivers = audiodrivers & (~AUDIO_PULSE);
		}
	}
#endif
#if defined(ESD)
	if((audiodrivers & AUDIO_ESD) && !FPX_esd_play_stream_fallback) {
		handle = dlopen("/usr/lib/libesd.so.0", RTLD_LAZY);
		if (!handle) {
			if(audiodebug) fprintf(stderr,"Can't load /usr/lib/libesd.so.0: %s\n",dlerror());
			audiodrivers = audiodrivers & (~AUDIO_ESD);
		}
		FPX_esd_play_stream_fallback = dlsym(handle, "esd_play_stream_fallback");
		if ((error = dlerror()) != NULL) {
			if(audiodebug) fprintf(stderr, "%s\n", error);
			audiodrivers = audiodrivers & (~AUDIO_ESD);
		}
	}
#endif

	return;
}

static void *FPX_SoundOutput_Open(void)
{
	void *ptr;

	if(audiodebug) fprintf( stderr,"audiodrivers=%d\n",audiodrivers);	
#if defined(PULSEAUDIO)
	if(audiodrivers & AUDIO_PULSE) {
		if(audiodebug) fprintf( stderr,"Trying PULSE\n");	
		ptr=PULSEAUDIO_FPX_SoundOutput_Open();
		if(audiodebug) fprintf( stderr,"ptr=%d\n",(int)ptr);	
		if(ptr) {
			audiotype=AUDIO_PULSE;
			if(audiodebug) fprintf( stderr, "Using PulseAudio driver\n");
			return ptr;
		}
	}
#endif
#if defined(ESD)
	if(audiodrivers & AUDIO_ESD) {
		if(audiodebug) fprintf( stderr,"Trying ESD\n");	
		ptr=ESD_FPX_SoundOutput_Open();
		if(audiodebug) fprintf( stderr,"ptr=%d\n",(int)ptr);	
		if(ptr) {
			audiotype=AUDIO_ESD;
			if(audiodebug) fprintf( stderr, "Using Esound audio driver\n");
			return ptr;
		}
	}
#endif
#if defined(ALSA_INTERNAL)
	if(audiodrivers & AUDIO_ALSA) {
		if(audiodebug) fprintf( stderr,"Using *INTERNAL* ALSA\n");	
		return 0;
	}
#endif
#if defined(ALSA)
	if(audiodrivers & AUDIO_ALSA) {
		if(audiodebug) fprintf( stderr,"Trying ALSA\n");	
		ptr=ALSA_FPX_SoundOutput_Open();
		if(audiodebug) fprintf( stderr,"ptr=%d\n",(int)ptr);	
		if(ptr) {
			audiotype=AUDIO_ALSA;
			if(audiodebug) fprintf( stderr, "Using ALSA audio driver\n");
			return ptr;
		}
	}
#endif
#if defined(OSS)
	if(audiodrivers & AUDIO_OSS) {
		if(audiodebug) fprintf( stderr,"Trying OSS\n");	
		ptr=OSS_FPX_SoundOutput_Open();
		if(audiodebug) fprintf( stderr,"ptr=%d\n",(int)ptr);	
		if(ptr) {
			audiotype=AUDIO_OSS;
			if(audiodebug) fprintf( stderr, "Using OSS audio driver\n");
			return ptr;
		}
	}
#endif
	// No sound...
	if(audiodebug) fprintf( stderr, "NO SOUND DRIVER! Revert to internal ALSA driver!\n");
	return 0;
}

static int FPX_SoundOutput_Close(void *ptr)
{
	int retcode;
#if defined(PULSEAUDIO)
	if(audiotype == AUDIO_PULSE) {
		retcode=PULSEAUDIO_FPX_SoundOutput_Close(ptr);
		return retcode;
	}
#endif
#if defined(ESD)	
	if(audiotype == AUDIO_ESD) {
		retcode=ESD_FPX_SoundOutput_Close(ptr);
		return retcode;
	}
#endif
#if defined(ALSA)
	if(audiotype == AUDIO_ALSA) {
		retcode=ALSA_FPX_SoundOutput_Close(ptr);
		return retcode;
	}
#endif
#if defined(OSS)	
	if(audiotype == AUDIO_OSS) {
		retcode=OSS_FPX_SoundOutput_Close(ptr);
		return retcode;
	}
#endif
	return 0;
}

static int FPX_SoundOutput_Latency(void *ptr)
{
	int retcode;
#if defined(PULSEAUDIO)
	if(audiotype == AUDIO_PULSE) {
		retcode=PULSEAUDIO_FPX_SoundOutput_Latency(ptr);
		return retcode;
	}
#endif
#if defined(ESD)
	if(audiotype == AUDIO_ESD) {
		retcode=ESD_FPX_SoundOutput_Latency(ptr);
		return retcode;
	}
#endif
#if defined(ALSA)	
	if(audiotype == AUDIO_ALSA) {
		retcode=ALSA_FPX_SoundOutput_Latency(ptr);
		return retcode;
	}
#endif
#if defined(OSS)	
	if(audiotype == AUDIO_OSS) {
		retcode=OSS_FPX_SoundOutput_Latency(ptr);
		return retcode;
	}
#endif
	return 0;
}
#endif // defined(ALSA) || defined(OSS) || defined(ESD) || defined(PULSEAUDIO)

void *FPX_Init(void *ptr)
{
#if defined(ALSA) || defined(OSS) || defined(ESD) || defined(PULSEAUDIO)
	FPX_SoundOutput_Detect();
#endif // defined(ALSA) || defined(OSS) || defined(ESD) || defined(PULSEAUDIO)
	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(ESD) || defined(PULSEAUDIO)
	if ( fpi_functions->fpi_count >= 3 )	FPI_SoundOutput_FillBuffer= fpi_functions->fpi_soundoutput_fillbuffer;  // 3
#endif // defined(ALSA) || defined(OSS) || defined(ESD) || defined(PULSEAUDIO)

	//
	// 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)

#ifdef ICU
	fpx_functions.fpx_unicode_codepagetoutf8 	= FPX_Unicode_CodePageToUTF8;	// 7
	fpx_functions.fpx_unicode_utf8tocodepage 	= FPX_Unicode_UTF8ToCodePage;	// 8
#endif // ICU


#if defined(ALSA) || defined(OSS) || defined(ESD) || defined(PULSEAUDIO)
	fpx_functions.fpx_soundoutput_open 			= FPX_SoundOutput_Open;			// 9
	fpx_functions.fpx_soundoutput_close 		= FPX_SoundOutput_Close;		// 10
	fpx_functions.fpx_soundoutput_latency 		= FPX_SoundOutput_Latency;		// 11
#endif // defined(ALSA) || defined(OSS) || defined(ESD) || defined(PULSEAUDIO)

#ifdef V4L1
	fpx_functions.fpx_videoinput_open			= FPX_VideoInput_Open;			// 12
	fpx_functions.fpx_videoinput_close			= FPX_VideoInput_Close;			// 13
	fpx_functions.fpx_videoinput_getframe		= FPX_VideoInput_GetFrame;		// 14
#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

//
// Unicode support functions
//

#ifdef ICU
static char *FPX_Unicode_CodePageToUTF8(const char *buffer, int code_page)
	// buffer = null termined input string, encoded using specified code page
	// code_page = windows codepage id
	// return value = null terminated, UTF8 encoded string. Use FPI_Mem_Alloc to allocate string. String will be freed by caller.
{
	UErrorCode err = U_UNSUPPORTED_ERROR;
	UConverter *codepage_conv = 0;
	UConverter *utf8_conv = 0;

	UChar *temp_buf = 0;
	int32_t temp_buf_len = 0;
	int32_t temp_len = 0;

	char *utf8_buf = 0;
	int32_t utf8_buf_len = 0;
	int32_t utf8_len = 0;

	char code_page_str[16];

	snprintf(code_page_str,16,"cp%d",code_page);
	code_page_str[15] = 0;

	if ( ( codepage_conv = ucnv_open(code_page_str,&err) ) == 0 ) {
		if ( ( codepage_conv = ucnv_open(0,&err) ) == 0 ) {
			goto fail;
		}
	}

	if ( ( utf8_conv = ucnv_open("utf8",&err) ) == 0 ) {
		goto fail;
	}

	temp_buf_len = sizeof(UChar)*strlen(buffer)*2;
	temp_buf = (UChar *)FPI_Mem_Alloc(temp_buf_len);
	temp_len = ucnv_toUChars(codepage_conv, temp_buf, temp_buf_len, buffer, strlen(buffer), &err);

	if ( temp_len > 0 ) {

		utf8_buf_len = UCNV_GET_MAX_BYTES_FOR_STRING(temp_len, ucnv_getMaxCharSize(utf8_conv));
		utf8_buf = (char *)FPI_Mem_Alloc(utf8_buf_len);
		utf8_len = ucnv_fromUChars(utf8_conv, utf8_buf, utf8_buf_len, temp_buf, temp_len, &err);

		if ( utf8_len <= 0 ) {
			FPI_Mem_Free(utf8_buf);
			utf8_buf = 0;
		}

	}

	FPI_Mem_Free(temp_buf);

fail:
	if ( codepage_conv ) {
		ucnv_close(codepage_conv);
	}
	if ( utf8_conv ) {
		ucnv_close(utf8_conv);
	}
	return utf8_buf;
}

static char *FPX_Unicode_UTF8ToCodePage(const char *buffer, int code_page)
	// buffer = null terminated input string, encoded using UTF8
	// code_page = windows codepage id
	// return value = null terminated string encoded using specified code page. Use FPI_Mem_Alloc to allocate string. String will be freed by caller.
{
	UErrorCode err = U_UNSUPPORTED_ERROR;
	UConverter *codepage_conv = 0;
	UConverter *utf8_conv = 0;

	UChar *temp_buf = 0;
	int32_t temp_buf_len = 0;
	int32_t temp_len = 0;

	char *codepage_buf = 0;
	int32_t codepage_buf_len = 0;
	int32_t codepage_len = 0;

	char code_page_str[16];

	snprintf(code_page_str,16,"cp%d",code_page);
	code_page_str[15] = 0;

	if ( ( codepage_conv = ucnv_open(code_page_str,&err) ) == 0 ) {
		if ( ( codepage_conv = ucnv_open(0,&err) ) == 0 ) {
			goto fail;
		}
	}

	if ( ( utf8_conv = ucnv_open("utf8",&err) ) == 0 ) {
		goto fail;
	}

	temp_buf_len = sizeof(UChar)*strlen(buffer)*2;
	temp_buf = (UChar *)FPI_Mem_Alloc(temp_buf_len);
	temp_len = ucnv_toUChars(utf8_conv, temp_buf, temp_buf_len, buffer, strlen(buffer), &err);

	if ( temp_len > 0 ) {

		codepage_buf_len = UCNV_GET_MAX_BYTES_FOR_STRING(temp_len, ucnv_getMaxCharSize(codepage_conv));
		codepage_buf = (char *)FPI_Mem_Alloc(codepage_buf_len);
		codepage_len = ucnv_fromUChars(codepage_conv, codepage_buf, codepage_buf_len, temp_buf, temp_len, &err);

		if ( codepage_len <= 0 ) {
			FPI_Mem_Free(codepage_buf);
			codepage_buf = 0;
		}
	}

	FPI_Mem_Free(temp_buf);

fail:
	if ( codepage_conv ) {
		ucnv_close(codepage_conv);
	}
	if ( utf8_conv ) {
		ucnv_close(utf8_conv);
	}
	return codepage_buf;
}
#endif // ICU

//
// Sound support functions
//


#ifdef ALSA
struct ALSA_SoundOutput_Instance {
	snd_pcm_t *				handle;
	int						signal;
	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;
};

static int alsa_error(void *ptr, int error)
{
	struct ALSA_SoundOutput_Instance *instance = (struct ALSA_SoundOutput_Instance *)ptr;
	switch( error ) {
		case	-EBADFD:
				return 0;
		case	-EAGAIN:
				return 0;
		case	-EPIPE:
				if ( snd_pcm_prepare(instance->handle) < 0) {
					return -1;
				}
				return 0;
		case	-ESTRPIPE:
				if ( snd_pcm_resume(instance->handle) < 0) {
					return -1;
				}
				return 0;
		default:
				return -1;
	}
}

static void *alsa_thread(void *ptr)
{
	struct ALSA_SoundOutput_Instance *instance = ptr;
	int result = 0;
	int err = 0;
	int state = 0;

	for( ; ; ) {
		snd_pcm_sframes_t avail;

		err = sem_wait(&instance->semaphore);
		if ( instance->signal ) {
			break;
		}

		if ( err < 0 ) {
			usleep(1);
			continue;
		}

		do {
			state = snd_pcm_state(instance->handle);
			if(state != SND_PCM_STATE_RUNNING &&
			   state != SND_PCM_STATE_PREPARED) {
				snd_pcm_prepare(instance->handle);
				break;
			}

			FPI_SoundOutput_FillBuffer(ptr, instance->buffer,
				snd_pcm_frames_to_bytes(instance->handle,
										instance->period_size));

			result = snd_pcm_writei(instance->handle, instance->buffer,
									instance->period_size);
			if(result < 0) {
				alsa_error(ptr, result);
			}

			avail = snd_pcm_avail_update(instance->handle);
			if(avail < 0) {
				if(alsa_error(ptr, avail) >= 0) {
					avail = snd_pcm_avail_update(instance->handle);
				}
			}
		} while(avail >= instance->period_size);
	}

	return NULL;
}

static void alsa_callback(snd_async_handler_t *ahandler)
{
	struct ALSA_SoundOutput_Instance *instance = (struct ALSA_SoundOutput_Instance *)snd_async_handler_get_callback_private(ahandler);
	// signal mixer thread
	sem_post(&instance->semaphore);
}

static void *ALSA_FPX_SoundOutput_Open()
	// return = instance pointer
{
	struct ALSA_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;

	if ( !FPI_SoundOutput_FillBuffer )  goto fail;
	if ( !FPI_Mem_Alloc ) goto fail;

	instance = FPI_Mem_Alloc(sizeof(struct ALSA_SoundOutput_Instance));
	memset(instance,0,sizeof(struct ALSA_SoundOutput_Instance));

	if ( ( snd_pcm_open(&instance->handle, "default", SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK) ) < 0) {
		if ( ( 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 (sem_init(&instance->semaphore, 0, 0) < 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_close(instance->handle);
		}
		if ( instance->buffer ) {
			if ( FPI_Mem_Free ) FPI_Mem_Free(instance->buffer);
		}
		if ( FPI_Mem_Free ) FPI_Mem_Free(instance);
	}
	return 0;
}

static int ALSA_FPX_SoundOutput_Close(void *ptr)
	// ptr = instance pointer
	// return = 0 on success, < 0 on error
{
	struct ALSA_SoundOutput_Instance *instance = (struct ALSA_SoundOutput_Instance *)ptr;
	if ( instance->handle ) {
		instance->signal = 1;
		sem_post(&instance->semaphore);
		pthread_join(instance->thread, NULL);
		snd_pcm_drop(instance->handle);
		snd_pcm_close(instance->handle);
		sem_destroy(&instance->semaphore);
	}
	if ( instance->buffer ) {
		if ( FPI_Mem_Free ) FPI_Mem_Free(instance->buffer);
	}
	if ( FPI_Mem_Free ) FPI_Mem_Free(instance);
	return 0;
}

static int ALSA_FPX_SoundOutput_Latency(void *ptr)
	// ptr = instance pointer
	// return = 0 on success, < 0 on error
{
	struct ALSA_SoundOutput_Instance *instance = (struct ALSA_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;
}
#endif

#if defined(OSS)
struct OSS_SoundOutput_Instance {
	int						oss_fd;
	pthread_t 				thread;
	int						signal;
};

static void *oss_thread(void *ptr)
{
	struct OSS_SoundOutput_Instance *instance = (struct OSS_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 *OSS_FPX_SoundOutput_Open()
	// return = instance pointer
{
	struct OSS_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 OSS_SoundOutput_Instance *)FPI_Mem_Alloc(sizeof(struct OSS_SoundOutput_Instance));
	memset(instance,0,sizeof(struct OSS_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 OSS_FPX_SoundOutput_Close(void *ptr)
	// ptr = instance pointer
	// return = 0 on success, < 0 on error
{
	struct OSS_SoundOutput_Instance *instance = (struct OSS_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 OSS_FPX_SoundOutput_Latency(void *ptr)
	// ptr = instance pointer
	// return = 0 on success, < 0 on error
{
	struct OSS_SoundOutput_Instance *instance = (struct OSS_SoundOutput_Instance *)ptr;
	if ( instance->oss_fd ) {
		int value = 0;
		if ( ioctl(instance->oss_fd,SNDCTL_DSP_GETODELAY,&value) == 0 ) {
			return value / 4;
		}
		return 0;
	}
	return -1;
}
#endif

#if defined(ESD)
struct ESD_SoundOutput_Instance {
	int						esd_sock;
	pthread_t 				thread;
	int						signal;
};

static void *esd_thread(void *ptr)
{
	struct ESD_SoundOutput_Instance *instance = (struct ESD_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->esd_sock, buffer, len);
			if ( written >= 0 ) {
				len -= written;
			}
			if ( instance->signal ) {
				pthread_exit(0);
			}
			if ( written < 0 ) {
				usleep(100);
			}
		}
	}
}

static void *ESD_FPX_SoundOutput_Open()
	// return = instance pointer
{
	struct ESD_SoundOutput_Instance *instance = 0;

	int rate = ESD_DEFAULT_RATE;

	int bits = ESD_BITS16, channels = ESD_STEREO;
	int mode = ESD_STREAM, func = ESD_PLAY ;
	esd_format_t format = 0;

	char *host = NULL;
	char *name = NULL;

	if ( !FPI_SoundOutput_FillBuffer )  goto fail;
	if ( !FPI_Mem_Alloc ) goto fail;

	format = bits | channels | mode | func;

	instance = (struct ESD_SoundOutput_Instance *)FPI_Mem_Alloc(sizeof(struct ESD_SoundOutput_Instance));
	memset(instance,0,sizeof(struct ESD_SoundOutput_Instance));
	instance->esd_sock=-1;

	if(audiodebug) fprintf( stderr, "opening socket, format = 0x%08x at %d Hz\n",format, rate );
                
	if ( ( instance->esd_sock = FPX_esd_play_stream_fallback( format, rate, host, name )) <= 0 ) goto fail;

	if ( pthread_create(&instance->thread, 0, esd_thread, instance) < 0 ) goto fail;

	return instance;
fail:
	if ( instance ) {
		if ( FPI_Mem_Free ) FPI_Mem_Free(instance);
	}
	return 0;
}

static int ESD_FPX_SoundOutput_Close(void *ptr)
	// ptr = instance pointer
	// return = 0 on success, < 0 on error
{
	struct ESD_SoundOutput_Instance *instance = (struct ESD_SoundOutput_Instance *)ptr;
	void *retVal = 0;

	instance->signal = 1;

	if ( instance->thread ) {
		pthread_join(instance->thread,&retVal);
	}

	if ( instance->esd_sock ) {
		close(instance->esd_sock);
	}

	if ( FPI_Mem_Free ) FPI_Mem_Free(instance);

	return 0;
}

static int ESD_FPX_SoundOutput_Latency(void *ptr)
	// ptr = instance pointer
	// return = 0 on success, < 0 on error
{
	
	/* JMD: this doesn't work. I think it's due to the handling
	   of signals in esdlib.c ... It just *hangs* via network...
	struct ESD_SoundOutput_Instance *instance = (struct ESD_SoundOutput_Instance *)ptr;
	if ( instance->esd_sock ) {
		return (esd_get_latency(instance->esd_sock));
	}
        */
	return -1;
}
#endif

#if defined(PULSEAUDIO)
struct PULSE_SoundOutput_Instance {
	pa_simple				*pa_sock;
	pthread_t 				thread;
	int					signal;
};

static void *pa_thread(void *ptr)
{
	struct PULSE_SoundOutput_Instance *instance = (struct PULSE_SoundOutput_Instance *)ptr;
	char buffer[4096];
	size_t len = 0;
	int error;
	for(;;) {
		FPI_SoundOutput_FillBuffer(ptr,buffer,4096);
		len = 4096;
		if (FPX_pa_simple_write(instance->pa_sock, buffer, len, &error) < 0) {
			if(audiodebug) fprintf(stderr, __FILE__": pa_simple_write() failed: %s\n", FPX_pa_strerror(error));
			usleep(100);
		}
		if ( instance->signal ) {
			pthread_exit(0);
		}
	}
}


static void *PULSEAUDIO_FPX_SoundOutput_Open()
	// return = instance pointer
{
	struct PULSE_SoundOutput_Instance *instance = 0;
	int error;
	if(audiodebug) fprintf( stderr, "Called FPX_SoundOutput_Open\n");

	/* The Sample format to use */
	static const pa_sample_spec ss = {
		.format = PA_SAMPLE_S16LE,
		.rate = 44100,
		.channels = 2
	};

	if ( !FPI_SoundOutput_FillBuffer )  goto fail;
	if ( !FPI_Mem_Alloc ) goto fail;

	instance = (struct PULSE_SoundOutput_Instance *)FPI_Mem_Alloc(sizeof(struct PULSE_SoundOutput_Instance));
	memset(instance,0,sizeof(struct PULSE_SoundOutput_Instance));

	if ( ( instance->pa_sock = FPX_pa_simple_new(NULL, "flashplayer", PA_STREAM_PLAYBACK, NULL, "playback", &ss, NULL, NULL, &error)) <= 0 ) goto fail;
	if(audiodebug) fprintf( stderr, "Opened Pulse\n");

	if ( pthread_create(&instance->thread, 0, pa_thread, instance) < 0 ) goto fail;

	return instance;
fail:
	if(audiodebug) fprintf( stderr, "FAILED TO OPEN PULSEAUDIO!\n");
	if ( instance ) {
		if ( FPI_Mem_Free ) FPI_Mem_Free(instance);
	}
	return 0;
}

static int PULSEAUDIO_FPX_SoundOutput_Close(void *ptr)
	// ptr = instance pointer
	// return = 0 on success, < 0 on error
{
	struct PULSE_SoundOutput_Instance *instance = (struct PULSE_SoundOutput_Instance *)ptr;
	void *retVal = 0;
	int error;

	instance->signal = 1;

	if ( instance->thread ) {
		pthread_join(instance->thread,&retVal);
	}

	if ( instance->pa_sock ) {
		FPX_pa_simple_drain(instance->pa_sock, &error);
		FPX_pa_simple_free(instance->pa_sock);
	}

	if ( FPI_Mem_Free ) FPI_Mem_Free(instance);

	return 0;
}

static int PULSEAUDIO_FPX_SoundOutput_Latency(void *ptr)
	// ptr = instance pointer
	// return = 0 on success, < 0 on error
{
	
	pa_usec_t latencytime=0;
	float latencysamples=0.0;
	int latency=0;
	int error;
	struct PULSE_SoundOutput_Instance *instance = (struct PULSE_SoundOutput_Instance *)ptr;
	if ( instance->pa_sock ) {
		latencytime = FPX_pa_simple_get_latency(instance->pa_sock, &error);
	}
	if(pulsedebug) fprintf(stderr, "\nLatency: %0.0f usec    \n", (float)latencytime);
	/* Max bytes in one second:
	   sample_rate*num_of_channels*sample_size_in_bytes
	   44100 * 2 (stereo) * 2 (16 bit samples)
	   Magic number = 176400
	   
	   Max frames in one second:
	   sample_rate: 44100
	   Magic number = 44100
	   
	   Now, convert pulse's latency time to number of bytes as
	   required by FlashPlayer
	   Latency = magic number * latencytime / 1000000 (for microseconds)
	*/
	latencysamples= 44100 * latencytime / 1000000;
	latency=(int)latencysamples;
	if(pulsedebug) fprintf(stderr, "\nLatency: %d samples\n", latency);
	return latency;
	return -1;
}
#endif // defined(ALSA/OSS/ESD/PULSEAUDIO)

#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
