Mercurial > mplayer.hg
view libao2/ao_sndio.c @ 37041:8aa4f25a4c17
mplayer: Fix potential NULL dereference.
author | reimar |
---|---|
date | Sun, 06 Apr 2014 18:46:45 +0000 |
parents | 38234a308fd3 |
children |
line wrap: on
line source
/* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * * 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 * with MPlayer; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include <sys/types.h> #include <poll.h> #include <errno.h> #include <sndio.h> #include <stdlib.h> #include "config.h" #include "mp_msg.h" #include "mixer.h" #include "help_mp.h" #include "libaf/af_format.h" #include "audio_out.h" #include "audio_out_internal.h" static ao_info_t info = { "sndio audio output", "sndio", "Alexandre Ratchov <alex@caoua.org>", "" }; LIBAO_EXTERN(sndio) static struct sio_hdl *hdl; static struct pollfd *pfds; static struct sio_par par; static int delay, vol, havevol; static int prepause_delay; /* * control misc parameters (only the volume for now) */ static int control(int cmd, void *arg) { ao_control_vol_t *ctl = arg; switch (cmd) { case AOCONTROL_GET_VOLUME: if (!havevol) return CONTROL_FALSE; ctl->left = ctl->right = vol * 100 / SIO_MAXVOL; break; case AOCONTROL_SET_VOLUME: if (!havevol) return CONTROL_FALSE; sio_setvol(hdl, ctl->left * SIO_MAXVOL / 100); break; default: return CONTROL_UNKNOWN; } return CONTROL_OK; } /* * call-back invoked whenever the the hardware position changes */ static void movecb(void *addr, int delta) { delay -= delta * (int)(par.bps * par.pchan); } /* * call-back invoked whenever the volume changes */ static void volcb(void *addr, unsigned newvol) { vol = newvol; } /* * open device and setup parameters * return: 1 = success, 0 = failure */ static int init(int rate, int channels, int format, int flags) { int bpf; hdl = sio_open(SIO_DEVANY, SIO_PLAY, 0); if (hdl == NULL) { mp_msg(MSGT_AO, MSGL_ERR, "ao2: can't open sndio\n"); return 0; } sio_initpar(&par); par.bits = af_fmt2bits(format); par.bps = (par.bits + 7) >> 3; // normally bits == 8*bps so this makes no difference // but we can support more formats for msb == 1, see "if" below par.msb = 1; par.sig = (format & AF_FORMAT_SIGN_MASK) == AF_FORMAT_SI; if (par.bits > 8) par.le = (format & AF_FORMAT_END_MASK) == AF_FORMAT_LE; par.rate = rate; par.pchan = channels; par.appbufsz = par.rate * 250 / 1000; /* 250ms buffer */ par.round = par.rate * 10 / 1000; /* 10ms block size */ if (!sio_setpar(hdl, &par)) { mp_msg(MSGT_AO, MSGL_ERR, "ao2: couldn't set params\n"); goto err_out; } if (!sio_getpar(hdl, &par)) { mp_msg(MSGT_AO, MSGL_ERR, "ao2: couldn't get params\n"); goto err_out; } // we do not care if LSBs are discarded if (par.bits < 8 * par.bps && !par.msb) { mp_msg(MSGT_AO, MSGL_ERR, "ao2: unsupported format\n"); goto err_out; } pfds = calloc(sio_nfds(hdl), sizeof(*pfds)); if (pfds == NULL) { mp_msg(MSGT_AO, MSGL_ERR, "ao2: couldn't allocate poll fds\n"); goto err_out; } bpf = par.bps * par.pchan; ao_data.format = af_bits2fmt(8 * par.bps); ao_data.format |= par.sig ? AF_FORMAT_SI : AF_FORMAT_US; ao_data.format |= par.le ? AF_FORMAT_LE : AF_FORMAT_BE; ao_data.channels = par.pchan; ao_data.bps = bpf * par.rate; ao_data.buffersize = par.bufsz * bpf; ao_data.outburst = par.round * bpf; ao_data.samplerate = rate; havevol = sio_onvol(hdl, volcb, NULL); sio_onmove(hdl, movecb, NULL); /* * prepare the device to start. It will start as soon there's enough * data in the buffer to not underrun */ delay = 0; if (!sio_start(hdl)) { mp_msg(MSGT_AO, MSGL_ERR, "ao2: init: couldn't start\n"); goto err_out; } return 1; err_out: free(pfds); pfds = NULL; sio_close(hdl); hdl = NULL; return 0; } /* * close device */ static void uninit(int immed) { if (hdl) sio_close(hdl); hdl = NULL; free(pfds); pfds = NULL; } /* * stop playing and prepare to restart */ static void reset(void) { if (!sio_stop(hdl)) mp_msg(MSGT_AO, MSGL_ERR, "ao2: reset: couldn't stop\n"); delay = 0; if (!sio_start(hdl)) mp_msg(MSGT_AO, MSGL_ERR, "ao2: reset: couldn't start\n"); } /* * refresh the "delay" counter: call sio_revents() which keeps track of * the hardware position */ static void refresh(void) { int n = sio_pollfd(hdl, pfds, POLLOUT); while (poll(pfds, n, 0) < 0 && errno == EINTR) ; /* nothing */ sio_revents(hdl, pfds); } /* * return the number of bytes available in the device buffer */ static int get_space(void) { refresh(); return par.bufsz * par.pchan * par.bps - delay; } /* * delay in seconds between first and last sample in the buffer */ static float get_delay(void) { refresh(); return (float)delay / (par.bps * par.pchan * par.rate); } /* * submit the given number of bytes to the device */ static int play(void *data, int len, int flags) { int n = sio_write(hdl, data, len); delay += n; return n; } /* * pause playing */ static void audio_pause(void) { /* * sndio can't pause, so just stop */ prepause_delay = delay; if (!sio_stop(hdl)) mp_msg(MSGT_AO, MSGL_ERR, "ao2: pause: couldn't stop\n"); delay = 0; } /* * resume playing after audio_pause() */ static void audio_resume(void) { /* * prepare to start; then refill the buffer with the number of bytes * audio_pause() consumed (this will trigger start) */ if (!sio_start(hdl)) mp_msg(MSGT_AO, MSGL_ERR, "ao2: resume: couldn't start\n"); mp_ao_resume_refill(&audio_out_sndio, par.bufsz * par.pchan * par.bps - prepause_delay); }