/* 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(void); 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 #ifdef OPENSSL #include #elif defined(GNUTLS) #include #include #include #include #include #endif // GNUTLS #ifdef ALSA #include #include #include #elif defined(OSS) #include #include #include #include #include #endif // OSS #ifdef V4L1 #include #include #include #include #include #endif // V4L1 #ifdef PULSEAUDIO #include #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(void) { #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 > 4) { size_t l = length; if (l > BUFSIZE) l = BUFSIZE; l &= ~ ((size_t) 3); 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; } /* There's no real handling of errors here. Unfortunately the * Flash API doesn't export a sane way to do this. With networked * audio streams and hotplug-capable audio devices the audio * stream might be killed in the middle of nothing, hence it is * very unfortunate that we cannot report errors that happen here * back to Flash. */ 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. This makes porting Flash to * Big-Endian machines unnecessary * difficult. */ .rate = 44100, .channels = 2 }; struct output_data *p; /* Unfortunately we cannot report any useful error string back to * Flash. It would be highly preferable if Flash supported some * way how we could tell the user what the reason is why audio is * not working for him. */ 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_disconnect(p->stream); pa_stream_unref(p->stream); } if (p->context) { pa_context_disconnect(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 = 0; 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(void) { 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