[MPlayer-dev-eng] [PATCH] [TESTREQUEST] ao_jack redone
Reimar Döffinger
Reimar.Doeffinger at stud.uni-karlsruhe.de
Wed May 18 19:51:07 CEST 2005
Hi,
I tried to fix some bugs and problems with ao_jack, but gave up and
instea redid it without using bio2jack (because some of the problems
were created by it and also 90% of what it does MPlayer can already do
(format, sampling rate conversion, volume control...).
Please test and comment, especially if you think you would miss bio2jack
(e.g. the support for libsamplerate it provides)...
Greetings,
Reimar Döffinger
-------------- next part --------------
Index: configure
===================================================================
RCS file: /cvsroot/mplayer/main/configure,v
retrieving revision 1.1004
diff -u -r1.1004 configure
--- configure 15 May 2005 20:11:33 -0000 1.1004
+++ configure 18 May 2005 17:18:42 -0000
@@ -356,7 +357,6 @@
--with-toolamedir=DIR path to Toolame library and include file
--with-xmmsplugindir=DIR XMMS plugins in DIR
--with-xmmslibdir=DIR libxmms.so.1 in DIR
- --with-bio2jack=DIR libbio2jack.a in DIR
--with-cdparanoiaincdir=DIR cdparanoia headers in DIR (*)
--with-cdparanoialibdir=DIR cdparanoia libraries (libcdda_*) in DIR (*)
--with-xvmclib=NAME name of adapter-specific library (e.g. XvMCNVIDIA)
@@ -1762,10 +1765,6 @@
_xmmsplugindir=`echo $ac_option | cut -d '=' -f 2`
;;
- --with-bio2jack=*)
- _bio2jackdir=`echo $ac_option | cut -d '=' -f 2`
- ;;
-
--enable-profile)
_profile='-p'
;;
@@ -4634,16 +4633,9 @@
cat > $TMPC << EOF
#include <jack/jack.h>
-int main(void) { JACK_Init(); return 0; }
+int main(void) { jack_client_new("test"); return 0; }
EOF
- # This test only checks the minor version number.
- if ( ( test `bio2jack-config --version | cut -d '.' -f 2` -ge 3 ) ) >/dev/null 2>&1 ; then
- if test -z "$_bio2jackdir" ; then
- cc_check -lbio2jack `pkg-config --libs --cflags jack` && ( "$TMPO" >> "$TMPLOG" 2>&1 ) && _jack=yes
- else
- cc_check -L "$_bio2jackdir" -lbio2jack `pkg-config --libs --cflags jack` && ( "$TMPO" >> "$TMPLOG" 2>&1 ) && _jack=yes
- fi
- fi
+ cc_check `pkg-config --libs --cflags jack` && ( "$TMPO" >> "$TMPLOG" 2>&1 ) && _jack=yes
fi
fi
@@ -4651,11 +4643,7 @@
_def_jack='#define USE_JACK 1'
_aosrc="$_aosrc ao_jack.c"
_aomodules="jack $_aomodules"
- if test -z "$_bio2jackdir" ; then
- _ld_jack="-lbio2jack `pkg-config --libs jack`"
- else
- _ld_jack="-L \"$_bio2jackdir\" -lbio2jack `pkg-config --libs jack`"
- fi
+ _ld_jack="`pkg-config --libs jack`"
_inc_jack=`pkg-config --cflags jack`
else
_noaomodules="jack $_noaomodules"
Index: libao2/ao_jack.c
===================================================================
RCS file: /cvsroot/mplayer/main/libao2/ao_jack.c,v
retrieving revision 1.8
diff -u -r1.8 ao_jack.c
--- libao2/ao_jack.c 25 Mar 2005 15:11:13 -0000 1.8
+++ libao2/ao_jack.c 18 May 2005 17:19:24 -0000
@@ -1,258 +1,237 @@
-/*
- * 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 (atmosfear at users.sf.net)
+ * and Reimar Döffinger (Reimar.Doeffinger at stud.uni-karlsruhe.de)
*/
#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 "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 <esack at browarek.net>",
- ""
+ "Reimar Döffinger <Reimar.Doeffinger at stud.uni-karlsruhe.de",
+ "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);
-}
-
-
-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);
-
- return 1;
-}
-
-
-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 play(void* data,int len,int flags)
-{
- return JACK_Write(driver, data, len);
-}
-
-
-static void audio_pause()
-{
- JACK_SetState(driver, PAUSED);
-}
-
-
-static void audio_resume()
-{
- JACK_SetState(driver, PLAYING);
-}
-
-
-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;
-}
-
-
-static int get_space()
-{
- return JACK_GetBytesFreeSpace(driver);
-}
-
-
-static float get_delay()
-{
- float ret = 0;
- ret = (JACK_GetBytesStored(driver) + approx_bytes_in_jackd + latency) / (float)ao_data.bps;
- return ret;
+#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;
+static volatile int underrun = 0;
+
+#define CHUNK_SIZE (16 * 1024)
+#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)
+
+static unsigned char *buffer = NULL;
+
+// may only be modified by SDL's playback thread or while it is stopped
+static volatile int read_pos;
+// may only be modified by mplayer's thread
+static volatile int write_pos;
+
+// 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;
+}
+
+// may only be called by SDL's 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 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;
+}
+
+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
+
+// to set/get/query special features/parameters
+static int control(int cmd,void *arg){
+ return CONTROL_UNKNOWN;
+}
+
+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;
+}
+
+// JACK Callback function
+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);
+ return 0;
+}
+
+static int init(int rate, int channels, int format, int flags) {
+ const char **phys_ports = NULL;
+ int i;
+ 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);
+ phys_ports = jack_get_ports (client, NULL, NULL, JackPortIsPhysical|JackPortIsInput);
+ for (num_ports = 0; phys_ports && phys_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;
+ 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]), phys_ports[i])) {
+ mp_msg(MSGT_AO, MSGL_FATAL, "[JACK] connecting failed\n");
+ goto err_out;
+ }
+ }
+ free(phys_ports);
+ 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_latency(ports[0]) * ao_data.bps;
+ ao_data.outburst = CHUNK_SIZE;
+ return 1;
+
+err_out:
+ free(phys_ports);
+ if (client)
+ jack_client_close (client);
+ return 0;
+}
+
+// close audio device
+static void uninit(int immed) {
+ if (!immed)
+ usec_sleep(get_delay() * 1000 * 1000);
+ paused = 1;
+ jack_client_close (client);
+ free(buffer);
+ buffer = NULL;
+}
+
+// stop playing and empty buffers (for seeking/pause)
+static void reset() {
+ paused = 1;
+ /* Reset ring-buffer state */
+ read_pos = 0;
+ write_pos = 0;
+ paused = 0;
+}
+
+// stop playing, keep buffers (for pause)
+static void audio_pause() {
+ paused = 1;
+}
+
+// resume playing, after audio_pause()
+static void audio_resume() {
+ paused = 0;
+}
+
+static int get_space() {
+ return buf_free();
+}
+
+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(){
+ int buffered = BUFFSIZE - CHUNK_SIZE - buf_free(); // could be less
+ return (float)(buffered + ao_data.buffersize)/(float)ao_data.bps;
}
More information about the MPlayer-dev-eng
mailing list