Author: randy Date: 2006-01-10 22:25:12 -0700 (Tue, 10 Jan 2006) New Revision: 1337
Added: trunk/MPlayer/MPlayer-1.0pre7try2-jack_fixes-1.patch Log: Added MPlayer Jack fixes patch Added: trunk/MPlayer/MPlayer-1.0pre7try2-jack_fixes-1.patch =================================================================== --- trunk/MPlayer/MPlayer-1.0pre7try2-jack_fixes-1.patch 2006-01-11 04:31:29 UTC (rev 1336) +++ trunk/MPlayer/MPlayer-1.0pre7try2-jack_fixes-1.patch 2006-01-11 05:25:12 UTC (rev 1337) @@ -0,0 +1,571 @@ +Submitted By: Randy McMurchy <randy_at_linuxfromscratch_dot_org> +Date: 2006-01-10 +Initial Package Version: 1.0pre7try2 +Upstream Status: From upstream +Origin: Upstream CVS +Description: Fixes Jack build issues (notes say that bioJack is + no longer required, but this is not confirmed) + + +diff -Naur MPlayer-1.0pre7try2-orig/libao2/ao_jack.c MPlayer-1.0pre7try2/libao2/ao_jack.c +--- MPlayer-1.0pre7try2-orig/libao2/ao_jack.c 2005-03-25 15:11:13.000000000 +0000 ++++ MPlayer-1.0pre7try2/libao2/ao_jack.c 2006-01-11 04:52:30.000000000 +0000 +@@ -1,258 +1,345 @@ +-/* +- * ao_jack - JACK audio output driver for MPlayer ++/* ++ * ao_jack.c - libao2 JACK Audio Output Driver for MPlayer + * +- * Kamil Strzelecki < esack at browarek.net > +- * +- * This driver is distribuited under terms of GPL +- * +- * It uses bio2jack (http://bio2jack.sf.net/). ++ * This driver is under the same license as MPlayer. ++ * (http://www.mplayerhq.hu) + * ++ * Copyleft 2001 by Felix B�nemann ([EMAIL PROTECTED]) ++ * and Reimar D�ffinger ([EMAIL PROTECTED]) + */ + + #include <stdio.h> +-#include <jack/jack.h> ++#include <stdlib.h> ++#include <string.h> + + #include "config.h" ++#include "mp_msg.h" ++#include "help_mp.h" ++ + #include "audio_out.h" + #include "audio_out_internal.h" + #include "libaf/af_format.h" +-#include "mp_msg.h" ++#include "osdep/timer.h" ++#include "subopt-helper.h" + +-//#include "bio2jack.h" +- +-static int driver = 0; +-long latency = 0; +-long approx_bytes_in_jackd = 0; +- +-//bio2jack stuff: +-#define ERR_SUCCESS 0 +-#define ERR_OPENING_JACK 1 +-#define ERR_RATE_MISMATCH 2 +-#define ERR_BYTES_PER_FRAME_INVALID 3 +-enum status_enum { PLAYING, PAUSED, STOPPED, CLOSED, RESET }; +-void JACK_Init(void); +-int JACK_Open(int* deviceID, unsigned int bits_per_sample, unsigned long *rate, int channels); +-int JACK_Close(int deviceID); /* return 0 for success */ +-void JACK_Reset(int deviceID); /* free all buffered data and reset several values in the device */ +-long JACK_Write(int deviceID, char *data, unsigned long bytes); /* returns the number of bytes written */ +-long JACK_GetJackLatency(int deviceID); /* return the latency in milliseconds of jack */ +-int JACK_SetState(int deviceID, enum status_enum state); /* playing, paused, stopped */ +-int JACK_SetAllVolume(int deviceID, unsigned int volume); +-int JACK_SetVolumeForChannel(int deviceID, unsigned int channel, unsigned int volume); +-void JACK_GetVolumeForChannel(int deviceID, unsigned int channel, unsigned int *volume); +-// ++#include "libvo/fastmemcpy.h" + ++#include <jack/jack.h> + +-static ao_info_t info = ++static ao_info_t info = + { +- "JACK audio output", +- "jack", +- "Kamil Strzelecki <[EMAIL PROTECTED]>", +- "" ++ "JACK audio output", ++ "jack", ++ "Reimar D�ffinger <[EMAIL PROTECTED]>", ++ "based on ao_sdl.c" + }; + +- + LIBAO_EXTERN(jack) + +- +-static int control(int cmd, void *arg) +-{ +- switch(cmd) { +- case AOCONTROL_GET_VOLUME: +- { +- ao_control_vol_t *vol = (ao_control_vol_t *)arg; +- unsigned int l, r; +- +- JACK_GetVolumeForChannel(driver, 0, &l); +- JACK_GetVolumeForChannel(driver, 1, &r); +- vol->left = (float )l; +- vol->right = (float )r; +- +- return CONTROL_OK; +- } +- case AOCONTROL_SET_VOLUME: +- { +- ao_control_vol_t *vol = (ao_control_vol_t *)arg; +- unsigned int l = (int )vol->left, +- r = (int )vol->right, +- avg_vol = (l + r) / 2, +- err = 0; +- +- switch (ao_data.channels) { +- case 6: +- if((err = JACK_SetVolumeForChannel(driver, 5, avg_vol))) { +- mp_msg(MSGT_AO, MSGL_ERR, +- "AO: [Jack] Setting lfe volume failed, error %d\n",err); +- return CONTROL_ERROR; +- } +- case 5: +- if((err = JACK_SetVolumeForChannel(driver, 4, avg_vol))) { +- mp_msg(MSGT_AO, MSGL_ERR, +- "AO: [Jack] Setting center volume failed, error %d\n",err); +- return CONTROL_ERROR; +- } +- case 4: +- if((err = JACK_SetVolumeForChannel(driver, 3, r))) { +- mp_msg(MSGT_AO, MSGL_ERR, +- "AO: [Jack] Setting rear right volume failed, error %d\n",err); +- return CONTROL_ERROR; +- } +- case 3: +- if((err = JACK_SetVolumeForChannel(driver, 2, l))) { +- mp_msg(MSGT_AO, MSGL_ERR, +- "AO: [Jack] Setting rear left volume failed, error %d\n",err); +- return CONTROL_ERROR; +- } +- case 2: +- if((err = JACK_SetVolumeForChannel(driver, 1, r))) { +- mp_msg(MSGT_AO, MSGL_ERR, +- "AO: [Jack] Setting right volume failed, error %d\n",err); +- return CONTROL_ERROR; +- } +- case 1: +- if((err = JACK_SetVolumeForChannel(driver, 0, l))) { +- mp_msg(MSGT_AO, MSGL_ERR, +- "AO: [Jack] Setting left volume failed, error %d\n",err); +- return CONTROL_ERROR; +- } +- } +- +- return CONTROL_OK; +- } +- } +- +- return(CONTROL_UNKNOWN); ++//! maximum number of channels supported, avoids lots of mallocs ++#define MAX_CHANS 6 ++static jack_port_t *ports[MAX_CHANS]; ++static int num_ports; ///< Number of used ports == number of channels ++static jack_client_t *client; ++static volatile int paused = 0; ///< set if paused ++static volatile int underrun = 0; ///< signals if an underrun occured ++ ++//! If this is defined try to make a more precise delay estimation. Will be slower. ++#undef JACK_ESTIMATE_DELAY ++#ifdef JACK_ESTIMATE_DELAY ++static volatile int callback_samples = 0; ++static volatile unsigned int callback_time = 0; ++#endif ++ ++//! size of one chunk, if this is too small MPlayer will start to "stutter" ++//! after a short time of playback ++#define CHUNK_SIZE (16 * 1024) ++//! number of "virtual" chunks the buffer consists of ++#define NUM_CHUNKS 8 ++// This type of ring buffer may never fill up completely, at least ++// one byte must always be unused. ++// For performance reasons (alignment etc.) one whole chunk always stays ++// empty, not only one byte. ++#define BUFFSIZE ((NUM_CHUNKS + 1) * CHUNK_SIZE) ++ ++//! buffer for audio data ++static unsigned char *buffer = NULL; ++ ++//! buffer read position, may only be modified by playback thread or while it is stopped ++static volatile int read_pos; ++//! buffer write position, may only be modified by MPlayer's thread ++static volatile int write_pos; ++ ++/** ++ * \brief get the number of free bytes in the buffer ++ * \return number of free bytes in buffer ++ * ++ * may only be called by MPlayer's thread ++ * return value may change between immediately following two calls, ++ * and the real number of free bytes might be larger! ++ */ ++static int buf_free() { ++ int free = read_pos - write_pos - CHUNK_SIZE; ++ if (free < 0) free += BUFFSIZE; ++ return free; + } + ++/** ++ * \brief get amount of data available in the buffer ++ * \return number of bytes available in buffer ++ * ++ * may only be called by the playback thread ++ * return value may change between immediately following two calls, ++ * and the real number of buffered bytes might be larger! ++ */ ++static int buf_used() { ++ int used = write_pos - read_pos; ++ if (used < 0) used += BUFFSIZE; ++ return used; ++} + +-static int init(int rate_hz, int channels, int format, int flags) +-{ +- int err, m, frag_spec; +- unsigned long rate; +- unsigned int bits_per_sample; +- +- unsigned long jack_port_flags=JackPortIsPhysical; +- unsigned int jack_port_name_count=0; +- const char *jack_port_name=NULL; +- +- mp_msg(MSGT_AO, MSGL_INFO, "AO: [Jack] Initialising library.\n"); +- JACK_Init(); +- +- if (ao_subdevice) { +- jack_port_flags = 0; +- jack_port_name_count = 1; +- jack_port_name = ao_subdevice; +- mp_msg(MSGT_AO, MSGL_INFO, "AO: [Jack] Trying to use jack device:%s.\n", ao_subdevice); +- } +- +- switch (format) { +- case AF_FORMAT_U8: +- case AF_FORMAT_S8: +- format = AF_FORMAT_U8; +- bits_per_sample = 8; +- m = 1; +- break; +- default: +- format = AF_FORMAT_S16_NE; +- bits_per_sample = 16; +- m = 2; +- break; +- } +- +- rate = rate_hz; +- +- err = JACK_OpenEx(&driver, bits_per_sample, &rate, channels, channels, +- &jack_port_name, jack_port_name_count, jack_port_flags); +- +- /* if sample rates doesn't match try to open device with jack's rate and +- * let mplayer convert it (rate now contains that which jackd use) */ +- if(err == ERR_RATE_MISMATCH) { +- mp_msg(MSGT_AO, MSGL_INFO, +- "AO: [Jack] Sample rate mismatch, trying to resample.\n"); +- +- err = JACK_OpenEx(&driver, bits_per_sample, &rate, channels, channels, +- &jack_port_name, jack_port_name_count, jack_port_flags); +- } +- +- /* any other error */ +- if(err != ERR_SUCCESS) { +- mp_msg(MSGT_AO, MSGL_ERR, +- "AO: [Jack] JACK_Open() failed, error %d\n", err); +- return 0; +- } +- +- err = JACK_SetAllVolume(driver, 100); +- if(err != ERR_SUCCESS) { +- // This is not fatal, but would be peculiar... +- mp_msg(MSGT_AO, MSGL_ERR, +- "AO: [Jack] JACK_SetAllVolume() failed, error %d\n", err); +- } +- +- latency = JACK_GetJackLatency(driver); +- +- ao_data.format = format; +- ao_data.channels = channels; +- ao_data.samplerate = rate; +- ao_data.bps = ( rate * channels * m ); +- +- // Rather rough way to find out the rough number of bytes buffered +- approx_bytes_in_jackd = JACK_GetJackBufferedBytes(driver) * 2; +- +- mp_msg(MSGT_AO, MSGL_INFO, +- "AO: [Jack] OK. I'm ready to go (%d Hz/%d channels/%d bit)\n", +- ao_data.samplerate, ao_data.channels, bits_per_sample); ++/** ++ * \brief insert len bytes into buffer ++ * \param data data to insert ++ * \param len length of data ++ * \return number of bytes inserted into buffer ++ * ++ * If there is not enough room, the buffer is filled up ++ */ ++static int write_buffer(unsigned char* data, int len) { ++ int first_len = BUFFSIZE - write_pos; ++ int free = buf_free(); ++ if (len > free) len = free; ++ if (first_len > len) first_len = len; ++ // till end of buffer ++ memcpy (&buffer[write_pos], data, first_len); ++ if (len > first_len) { // we have to wrap around ++ // remaining part from beginning of buffer ++ memcpy (buffer, &data[first_len], len - first_len); ++ } ++ write_pos = (write_pos + len) % BUFFSIZE; ++ return len; ++} + +- return 1; ++/** ++ * \brief read data from buffer and splitting it into channels ++ * \param bufs num_bufs float buffers, each will contain the data of one channel ++ * \param cnt number of samples to read per channel ++ * \param num_bufs number of channels to split the data into ++ * \return number of samples read per channel, equals cnt unless there was too ++ * little data in the buffer ++ * ++ * Assumes the data in the buffer is of type float, the number of bytes ++ * read is res * num_bufs * sizeof(float), where res is the return value. ++ */ ++static int read_buffer(float **bufs, int cnt, int num_bufs) { ++ int first_len = BUFFSIZE - read_pos; ++ int buffered = buf_used(); ++ int i, j; ++ if (cnt * sizeof(float) * num_bufs > buffered) ++ cnt = buffered / sizeof(float) / num_bufs; ++ for (i = 0; i < cnt; i++) { ++ for (j = 0; j < num_bufs; j++) { ++ bufs[j][i] = *((float *)(&buffer[read_pos])); ++ read_pos = (read_pos + sizeof(float)) % BUFFSIZE; ++ } ++ } ++ return cnt; + } + ++// end ring buffer stuff + +-static void uninit(int immed) +-{ +- int errval = 0; +- +- if((errval = JACK_Close(driver))) +- mp_msg(MSGT_AO, MSGL_ERR, +- "AO: [Jack] error closing device, error %d\n", errval); ++static int control(int cmd, void *arg) { ++ return CONTROL_UNKNOWN; + } + +- +-static int play(void* data,int len,int flags) +-{ +- return JACK_Write(driver, data, len); ++/** ++ * \brief fill the buffers with silence ++ * \param bufs num_bufs float buffers, each will contain the data of one channel ++ * \param cnt number of samples in each buffer ++ * \param num_bufs number of buffers ++ */ ++static void silence(float **bufs, int cnt, int num_bufs) { ++ int i, j; ++ for (i = 0; i < cnt; i++) ++ for (j = 0; j < num_bufs; j++) ++ bufs[j][i] = 0; + } + ++/** ++ * \brief JACK Callback function ++ * \param nframes number of frames to fill into buffers ++ * \param arg unused ++ * \return currently always 0 ++ * ++ * Write silence into buffers if paused or an underrun occured ++ */ ++static int outputaudio(jack_nframes_t nframes, void *arg) { ++ float *bufs[MAX_CHANS]; ++ int i; ++ for (i = 0; i < num_ports; i++) ++ bufs[i] = jack_port_get_buffer(ports[i], nframes); ++ if (!paused && !underrun) ++ if (read_buffer(bufs, nframes, num_ports) < nframes) ++ underrun = 1; ++ if (paused || underrun) ++ silence(bufs, nframes, num_ports); ++#ifdef JACK_ESTIMATE_DELAY ++ callback_samples = nframes; ++ callback_time = GetTimer(); ++#endif ++ return 0; ++} + +-static void audio_pause() ++/** ++ * \brief print suboption usage help ++ */ ++static void print_help () + { +- JACK_SetState(driver, PAUSED); ++ mp_msg (MSGT_AO, MSGL_FATAL, ++ "\n-ao jack commandline help:\n" ++ "Example: mplayer -ao jack:port=myout\n" ++ " connects MPlayer to the jack ports named myout\n" ++ "\nOptions:\n" ++ " port=<port name>\n" ++ " Connects to the given ports instead of the default physical ones\n"); + } + ++static int init(int rate, int channels, int format, int flags) { ++ const char **matching_ports = NULL; ++ char *port_name = NULL; ++ opt_t subopts[] = { ++ {"port", OPT_ARG_MSTRZ, &port_name, NULL}, ++ {NULL} ++ }; ++ int port_flags = JackPortIsInput; ++ int i; ++ if (subopt_parse(ao_subdevice, subopts) != 0) { ++ print_help(); ++ return 0; ++ } ++ if (channels > MAX_CHANS) { ++ mp_msg(MSGT_AO, MSGL_FATAL, "[JACK] Invalid number of channels: %i\n", channels); ++ goto err_out; ++ } ++ client = jack_client_new("MPlayer"); ++ if (!client) { ++ mp_msg(MSGT_AO, MSGL_FATAL, "[JACK] cannot open server\n"); ++ goto err_out; ++ } ++ jack_set_process_callback(client, outputaudio, 0); ++ ++ // list matching ports ++ if (!port_name) ++ port_flags |= JackPortIsPhysical; ++ matching_ports = jack_get_ports(client, port_name, NULL, port_flags); ++ for (num_ports = 0; matching_ports && matching_ports[num_ports]; num_ports++) ; ++ if (!num_ports) { ++ mp_msg(MSGT_AO, MSGL_FATAL, "[JACK] no physical ports available\n"); ++ goto err_out; ++ } ++ if (channels > num_ports) channels = num_ports; ++ num_ports = channels; ++ ++ // create out output ports ++ for (i = 0; i < num_ports; i++) { ++ char pname[30]; ++ snprintf(pname, 30, "out_%d", i); ++ ports[i] = jack_port_register(client, pname, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); ++ if (!ports[i]) { ++ mp_msg(MSGT_AO, MSGL_FATAL, "[JACK] not enough ports available\n"); ++ goto err_out; ++ } ++ } ++ if (jack_activate(client)) { ++ mp_msg(MSGT_AO, MSGL_FATAL, "[JACK] activate failed\n"); ++ goto err_out; ++ } ++ for (i = 0; i < num_ports; i++) { ++ if (jack_connect(client, jack_port_name(ports[i]), matching_ports[i])) { ++ mp_msg(MSGT_AO, MSGL_FATAL, "[JACK] connecting failed\n"); ++ goto err_out; ++ } ++ } ++ buffer = (unsigned char *) malloc(BUFFSIZE); ++ ++ ao_data.channels = channels; ++ ao_data.samplerate = rate = jack_get_sample_rate(client); ++ ao_data.format = AF_FORMAT_FLOAT_NE; ++ ao_data.bps = channels * rate * sizeof(float); ++ ao_data.buffersize = jack_port_get_total_latency(client, ports[0]) * channels; ++ ao_data.outburst = CHUNK_SIZE; ++ free(matching_ports); ++ free(port_name); ++ return 1; ++ ++err_out: ++ free(matching_ports); ++ free(port_name); ++ if (client) ++ jack_client_close(client); ++ free(buffer); ++ buffer = NULL; ++ return 0; ++} + +-static void audio_resume() +-{ +- JACK_SetState(driver, PLAYING); ++// close audio device ++static void uninit(int immed) { ++ if (!immed) ++ usec_sleep(get_delay() * 1000 * 1000); ++ // HACK, make sure jack doesn't loop-output dirty buffers ++ paused = 1; ++ usec_sleep(100 * 1000); ++ jack_client_close(client); ++ free(buffer); ++ buffer = NULL; + } + ++/** ++ * \brief stop playing and empty buffers (for seeking/pause) ++ */ ++static void reset() { ++ paused = 1; ++ read_pos = 0; ++ write_pos = 0; ++ paused = 0; ++} + +-static void reset() +-{ +- JACK_Reset(driver); +- latency = JACK_GetJackLatency(driver); +- // Rather rough way to find out the rough number of bytes buffered +- approx_bytes_in_jackd = JACK_GetJackBufferedBytes(driver) * 2; ++/** ++ * \brief stop playing, keep buffers (for pause) ++ */ ++static void audio_pause() { ++ paused = 1; + } + ++/** ++ * \brief resume playing, after audio_pause() ++ */ ++static void audio_resume() { ++ paused = 0; ++} + +-static int get_space() +-{ +- return JACK_GetBytesFreeSpace(driver); ++static int get_space() { ++ return buf_free(); + } + ++/** ++ * \brief write data into buffer and reset underrun flag ++ */ ++static int play(void *data, int len, int flags) { ++ len -= len % ao_data.outburst; ++ underrun = 0; ++ return write_buffer(data, len); ++} + +-static float get_delay() +-{ +- float ret = 0; +- ret = (JACK_GetBytesStored(driver) + approx_bytes_in_jackd + latency) / (float)ao_data.bps; +- return ret; ++static float get_delay() { ++ int buffered = BUFFSIZE - CHUNK_SIZE - buf_free(); // could be less ++ float in_jack = (float)ao_data.buffersize / (float)ao_data.bps; ++#ifdef JACK_ESTIMATE_DELAY ++ unsigned int elapsed = GetTimer() - callback_time; ++ in_jack += (float)callback_samples / (float)ao_data.samplerate - (float)elapsed / 1000.0 / 1000.0; ++ if (in_jack < 0) in_jack = 0; ++#endif ++ return (float)buffered / (float)ao_data.bps + in_jack; + } + -- http://linuxfromscratch.org/mailman/listinfo/patches FAQ: http://www.linuxfromscratch.org/faq/ Unsubscribe: See the above information page
