From d4bdd0473d6f43132257c9fb3848d829755167a3 Mon Sep 17 00:00:00 2001 From: wm4 Date: Mon, 5 Nov 2012 17:02:04 +0100 Subject: Rename directories, move files (step 1 of 2) (does not compile) Tis drops the silly lib prefixes, and attempts to organize the tree in a more logical way. Make the top-level directory less cluttered as well. Renames the following directories: libaf -> audio/filter libao2 -> audio/out libvo -> video/out libmpdemux -> demux Split libmpcodecs: vf* -> video/filter vd*, dec_video.* -> video/decode mp_image*, img_format*, ... -> video/ ad*, dec_audio.* -> audio/decode libaf/format.* is moved to audio/ - this is similar to how mp_image.* is located in video/. Move most top-level .c/.h files to core. (talloc.c/.h is left on top- level, because it's external.) Park some of the more annoying files in compat/. Some of these are relicts from the time mplayer used ffmpeg internals. sub/ is not split, because it's too much of a mess (subtitle code is mixed with OSD display and rendering). Maybe the organization of core is not ideal: it mixes playback core (like mplayer.c) and utility helpers (like bstr.c/h). Should the need arise, the playback core will be moved somewhere else, while core contains all helper and common code. --- audio/out/ao_jack.c | 361 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 361 insertions(+) create mode 100644 audio/out/ao_jack.c (limited to 'audio/out/ao_jack.c') diff --git a/audio/out/ao_jack.c b/audio/out/ao_jack.c new file mode 100644 index 0000000000..b30f99a14e --- /dev/null +++ b/audio/out/ao_jack.c @@ -0,0 +1,361 @@ +/* + * JACK audio output driver for MPlayer + * + * Copyleft 2001 by Felix Bünemann (atmosfear@users.sf.net) + * and Reimar Döffinger (Reimar.Doeffinger@stud.uni-karlsruhe.de) + * + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * along with MPlayer; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include + +#include "config.h" +#include "mp_msg.h" + +#include "audio_out.h" +#include "audio_out_internal.h" +#include "libaf/format.h" +#include "osdep/timer.h" +#include "subopt-helper.h" + +#include "libavutil/fifo.h" + +#include + +static const ao_info_t info = +{ + "JACK audio output", + "jack", + "Reimar Döffinger ", + "based on ao_sdl.c" +}; + +LIBAO_EXTERN(jack) + +//! maximum number of channels supported, avoids lots of mallocs +#define MAX_CHANS 8 +static jack_port_t *ports[MAX_CHANS]; +static int num_ports; ///< Number of used ports == number of channels +static jack_client_t *client; +static float jack_latency; +static int estimate; +static volatile int paused = 0; ///< set if paused +static volatile int underrun = 0; ///< signals if an underrun occured + +static volatile float callback_interval = 0; +static volatile float callback_time = 0; + +//! 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 +#define BUFFSIZE (NUM_CHUNKS * CHUNK_SIZE) + +//! buffer for audio data +static AVFifoBuffer *buffer; + +/** + * \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 free = av_fifo_space(buffer); + if (len > free) len = free; + return av_fifo_generic_write(buffer, data, len, NULL); +} + +static void silence(float **bufs, int cnt, int num_bufs); + +struct deinterleave { + float **bufs; + int num_bufs; + int cur_buf; + int pos; +}; + +static void deinterleave(void *info, void *src, int len) { + struct deinterleave *di = info; + float *s = src; + int i; + len /= sizeof(float); + for (i = 0; i < len; i++) { + di->bufs[di->cur_buf++][di->pos] = s[i]; + if (di->cur_buf >= di->num_bufs) { + di->cur_buf = 0; + di->pos++; + } + } +} + +/** + * \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. + * If there is not enough data in the buffer remaining parts will be filled + * with silence. + */ +static int read_buffer(float **bufs, int cnt, int num_bufs) { + struct deinterleave di = {bufs, num_bufs, 0, 0}; + int buffered = av_fifo_size(buffer); + if (cnt * sizeof(float) * num_bufs > buffered) { + silence(bufs, cnt, num_bufs); + cnt = buffered / sizeof(float) / num_bufs; + } + av_fifo_generic_read(buffer, &di, cnt * num_bufs * sizeof(float), deinterleave); + return cnt; +} + +// end ring buffer stuff + +static int control(int cmd, void *arg) { + return CONTROL_UNKNOWN; +} + +/** + * \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; + for (i = 0; i < num_bufs; i++) + memset(bufs[i], 0, cnt * sizeof(float)); +} + +/** + * \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) + silence(bufs, nframes, num_ports); + else + if (read_buffer(bufs, nframes, num_ports) < nframes) + underrun = 1; + if (estimate) { + float now = (float)GetTimer() / 1000000.0; + float diff = callback_time + callback_interval - now; + if ((diff > -0.002) && (diff < 0.002)) + callback_time += callback_interval; + else + callback_time = now; + callback_interval = (float)nframes / (float)ao_data.samplerate; + } + return 0; +} + +/** + * \brief print suboption usage help + */ +static void print_help (void) +{ + mp_msg (MSGT_AO, MSGL_FATAL, + "\n-ao jack commandline help:\n" + "Example: mpv -ao jack:port=myout\n" + " connects mpv to the jack ports named myout\n" + "\nOptions:\n" + " port=\n" + " Connects to the given ports instead of the default physical ones\n" + " name=\n" + " Client name to pass to JACK\n" + " estimate\n" + " Estimates the amount of data in buffers (experimental)\n" + " autostart\n" + " Automatically start JACK server if necessary\n" + ); +} + +static int init(int rate, int channels, int format, int flags) { + const char **matching_ports = NULL; + char *port_name = NULL; + char *client_name = NULL; + int autostart = 0; + const opt_t subopts[] = { + {"port", OPT_ARG_MSTRZ, &port_name, NULL}, + {"name", OPT_ARG_MSTRZ, &client_name, NULL}, + {"estimate", OPT_ARG_BOOL, &estimate, NULL}, + {"autostart", OPT_ARG_BOOL, &autostart, NULL}, + {NULL} + }; + jack_options_t open_options = JackUseExactName; + int port_flags = JackPortIsInput; + int i; + estimate = 1; + 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; + } + if (!client_name) { + client_name = malloc(40); + sprintf(client_name, "mpv [%d]", getpid()); + } + if (!autostart) + open_options |= JackNoStartServer; + client = jack_client_open(client_name, open_options, NULL); + if (!client) { + mp_msg(MSGT_AO, MSGL_FATAL, "[JACK] cannot open server\n"); + goto err_out; + } + buffer = av_fifo_alloc(BUFFSIZE); + 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); + if (!matching_ports || !matching_ports[0]) { + mp_msg(MSGT_AO, MSGL_FATAL, "[JACK] no physical ports available\n"); + goto err_out; + } + i = 1; + while (matching_ports[i]) i++; + if (channels > i) channels = i; + 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; + } + } + rate = jack_get_sample_rate(client); + jack_latency = (float)(jack_port_get_total_latency(client, ports[0]) + + jack_get_buffer_size(client)) / (float)rate; + callback_interval = 0; + + ao_data.channels = channels; + ao_data.samplerate = rate; + ao_data.format = AF_FORMAT_FLOAT_NE; + ao_data.bps = channels * rate * sizeof(float); + ao_data.buffersize = CHUNK_SIZE * NUM_CHUNKS; + ao_data.outburst = CHUNK_SIZE; + free(matching_ports); + free(port_name); + free(client_name); + return 1; + +err_out: + free(matching_ports); + free(port_name); + free(client_name); + if (client) + jack_client_close(client); + av_fifo_free(buffer); + buffer = NULL; + return 0; +} + +// 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 + reset(); + usec_sleep(100 * 1000); + jack_client_close(client); + av_fifo_free(buffer); + buffer = NULL; +} + +/** + * \brief stop playing and empty buffers (for seeking/pause) + */ +static void reset(void) { + paused = 1; + av_fifo_reset(buffer); + paused = 0; +} + +/** + * \brief stop playing, keep buffers (for pause) + */ +static void audio_pause(void) { + paused = 1; +} + +/** + * \brief resume playing, after audio_pause() + */ +static void audio_resume(void) { + paused = 0; +} + +static int get_space(void) { + return av_fifo_space(buffer); +} + +/** + * \brief write data into buffer and reset underrun flag + */ +static int play(void *data, int len, int flags) { + if (!(flags & AOPLAY_FINAL_CHUNK)) + len -= len % ao_data.outburst; + underrun = 0; + return write_buffer(data, len); +} + +static float get_delay(void) { + int buffered = av_fifo_size(buffer); // could be less + float in_jack = jack_latency; + if (estimate && callback_interval > 0) { + float elapsed = (float)GetTimer() / 1000000.0 - callback_time; + in_jack += callback_interval - elapsed; + if (in_jack < 0) in_jack = 0; + } + return (float)buffered / (float)ao_data.bps + in_jack; +} -- cgit v1.2.3