Beispiel #1
0
/**
 * \brief Wait for a PCM to become ready
 * \param pcm ordinary PCM handle
 * \param timeout maximum time in milliseconds to wait
 * \return a positive value on success otherwise a negative error code
 * \retval 0 timeout occurred
 * \retval 1 PCM stream is ready for I/O
 */
int sndo_pcm_wait(sndo_pcm_t *pcm, int timeout)
{
        struct pollfd pfd[2];
        unsigned short p_revents, c_revents;
        int err;
        err = snd_pcm_poll_descriptors(pcm->playback, &pfd[0], 1);
        assert(err == 1);
        err = snd_pcm_poll_descriptors(pcm->capture, &pfd[1], 1);
        assert(err == 1);
        err = poll(pfd, 2, timeout);
        if (err < 0)
                return -errno;
	if (err == 0)
		return 0;
	do {
	        err = snd_pcm_poll_descriptors_revents(pcm->playback, &pfd[0], 1, &p_revents);
		if (err < 0)
			return err;
		if (p_revents & (POLLERR | POLLNVAL))
			return -EIO;
		err = snd_pcm_poll_descriptors_revents(pcm->capture, &pfd[1], 1, &c_revents);
		if (err < 0)
			return err;
		if (c_revents & (POLLERR | POLLNVAL))
			return -EIO;
		if ((p_revents & POLLOUT) && (c_revents & POLLIN))
			return 1;
		err = poll(&pfd[(p_revents & POLLOUT) ? 1 : 0], 1, 1);
		if (err < 0)
			return err;
	} while (1);
}
Beispiel #2
0
void
alsa_check_fds(fd_set * rfds, fd_set * wfds)
{
	struct pollfd *f;
	int err;
	unsigned short revents;

	if (out_handle && !rdpsnd_queue_empty())
	{
		for (f = pfds_out; f < &pfds_out[num_fds_out]; f++)
		{
			f->revents = 0;
			if (f->fd != -1)
			{
				/* Fixme: This doesn't properly deal with things like POLLHUP */
				if (FD_ISSET(f->fd, rfds))
					f->revents |= POLLIN;
				if (FD_ISSET(f->fd, wfds))
					f->revents |= POLLOUT;
			}
		}

		err = snd_pcm_poll_descriptors_revents(out_handle, pfds_out, num_fds_out, &revents);
		if (err < 0)
			return;

		if (revents & POLLOUT)
			alsa_play();
	}


	if (in_handle)
	{
		for (f = pfds_in; f < &pfds_in[num_fds_in]; f++)
		{
			f->revents = 0;
			if (f->fd != -1)
			{
				/* Fixme: This doesn't properly deal with things like POLLHUP */
				if (FD_ISSET(f->fd, rfds))
					f->revents |= POLLIN;
				if (FD_ISSET(f->fd, wfds))
					f->revents |= POLLOUT;
			}
		}

		err = snd_pcm_poll_descriptors_revents(in_handle, pfds_in, num_fds_in, &revents);
		if (err < 0)
			return;

		if (revents & POLLIN)
			alsa_record();
	}
}
Beispiel #3
0
/* alsa_update:
 *  Updates main buffer in case ALSA is ready.
 */
static void alsa_update(int threaded)
{
   unsigned short revents;

   if (poll_next) {
      poll(ufds, pdc, 0);
      snd_pcm_poll_descriptors_revents(pcm_handle, ufds, pdc, &revents);
      if (revents & POLLERR) {
	 if (snd_pcm_state(pcm_handle) == SND_PCM_STATE_XRUN ||
	    snd_pcm_state(pcm_handle) == SND_PCM_STATE_SUSPENDED) {
	    int err = snd_pcm_state(pcm_handle) == SND_PCM_STATE_XRUN ? -EPIPE : -ESTRPIPE;
	    if (xrun_recovery(pcm_handle, err) < 0) {
	       fprintf(stderr, "Write error: %s\n", snd_strerror(err));
	    }
	    poll_next = 0;
         }
	 else {
	    fprintf(stderr, "Wait for poll failed\n");
	 }
         return;
      }
      if (!(revents & POLLOUT))
	 return;
   }
   alsa_mix();
}
Beispiel #4
0
APULSE_EXPORT
int
pa_mainloop_dispatch(pa_mainloop *m)
{
    trace_info("F %s m=%p\n", __func__, m);

    int cnt = 0;
    GList *keys = g_hash_table_get_keys(m->events_ht);
    GList *it;

    it = keys;
    while (it) {
        struct pa_io_event *ioe = it->data;
        if (ioe->pollfd && ioe->pollfd->revents) {
            int idx = ioe->pollfd - m->fds;
            unsigned short revents = 0;

            if (0 < idx && idx <= m->alsa_special_cnt) {
                snd_pcm_poll_descriptors_revents(ioe->pcm, ioe->pollfd, 1, &revents);
            } else {
                revents = ioe->pollfd->revents;
            }

            if (revents & (~(POLLOUT|POLLIN))) {
                recover_pcm(ioe->pcm);
            } else {
                pa_io_event_flags_t eflags = to_pa_io_event_flags(revents);
                if (ioe->cb)
                    ioe->cb(&m->api, ioe, ioe->fd, eflags, ioe->cb_userdata);
                ioe->pollfd->revents = 0;
                cnt ++;
            }
        }

        it = g_list_next(it);
    }
    g_list_free(keys);

    if (m->fds && m->fds[0].revents) {
        // drain wakeup pipe
        char buf[200];
        while (read(m->wakeup_pipe[0], buf, sizeof(buf)) > 0) {
            // cycle
        }
        m->fds[0].revents = 0;
    }

    pa_defer_event *de = g_queue_pop_head(m->queue);
    while (de) {
        if (de->cb)
            de->cb(&m->api, de, de->userdata);
        de = g_queue_pop_head(m->queue);
    }

    return cnt;
}
Beispiel #5
0
/**
 * \brief get returned events from poll descriptors
 * \param pcm ordinary pcm handle
 * \param pfds array of poll descriptors
 * \param nfds count of poll descriptors
 * \param revents returned events
 * \return zero if success, otherwise a negative error code
 */ 
int sndo_pcm_poll_descriptors_revents(sndo_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents)
{
	int playback, err;
	unsigned short _revents;

	playback = snd_pcm_poll_descriptors_count(pcm->playback);
	if (playback < 0)
		return playback;
	err = snd_pcm_poll_descriptors_revents(pcm->playback, pfds, nfds < (unsigned)playback ? nfds : (unsigned)playback, revents);
	if (err < 0)
		return err;
	if (nfds > (unsigned)playback) {
		err = snd_pcm_poll_descriptors_revents(pcm->capture, pfds + playback, nfds - playback, &_revents);
		if (err < 0)
			return err;
		*revents |= _revents;
	}
	return 0;
}
Beispiel #6
0
static int pcm_revents(struct alsa_pcm *alsa, unsigned short *revents) {
    int r;

    r = snd_pcm_poll_descriptors_revents(alsa->pcm, alsa->pe, alsa->pe_count,
                                         revents);
    if (r < 0) {
        alsa_error("poll_descriptors_revents", r);
        return -1;
    }
    
    return 0;
}
Beispiel #7
0
/* FIXME: proper lock? */
static void*
alsa_loop(void* arg) {
	int npfds = snd_pcm_poll_descriptors_count(alsa_pcm);
	struct pollfd* pfds;
	unsigned short* revents;

	if(npfds <= 0)
		goto _error;

	//printf("Starting alsa_loop thread...\n");

	pfds = alloca(sizeof(*pfds) * npfds);
	revents = alloca(sizeof(*revents) * npfds);

	while(going && alsa_pcm) {
		if(get_thread_buffer_filled() > prebuffer_size)
			prebuffer = false;

		if(!prebuffer && get_thread_buffer_filled() > hw_period_size_in) {
			snd_pcm_poll_descriptors(alsa_pcm, pfds, npfds);

			if(poll(pfds, npfds, 10) > 0) {
				/*
				 * need to check revents.  poll() with
				 * dmix returns a postive value even
				 * if no data is available
				 */
				int i;
				snd_pcm_poll_descriptors_revents(alsa_pcm, pfds,
				                                 npfds, revents);

				for(i = 0; i < npfds; i++)
					if(revents[i] & POLLOUT) {
						alsa_write_out_thread_data();
						break;
					}
			}
		} else
			usleep(10000);

		if(flush_request != -1) {
			alsa_do_flush(flush_request);
			flush_request = -1;
			prebuffer = true;
		}
	}

_error:
	alsa_close_pcm();
	free(thread_buffer);
	thread_buffer = NULL;
	pthread_exit(NULL);
}
Beispiel #8
0
static int wait_for_poll(snd_pcm_t *handle, struct pollfd *ufds, unsigned int count)
{
        unsigned short revents;

        while (1) {
                poll(ufds, count, -1);
                snd_pcm_poll_descriptors_revents(handle, ufds, count, &revents);
                if (revents & POLLERR)
                        return -EIO;
                if (revents & POLLOUT)
                        return 0;
        }
}
int alsa_device_playback_ready(AlsaDevice *dev, struct pollfd *pfds, unsigned int nfds)
{
   unsigned short revents=0;
   int err;
   if ((err = snd_pcm_poll_descriptors_revents(dev->playback_handle, pfds+dev->readN, dev->writeN, &revents)) < 0)
   {
      //cerr << "error in snd_pcm_poll_descriptors_revents for playback: " << snd_strerror (err) << endl;
      //FIXME: This is a kludge
      fprintf (stderr, "error in alsa_device_playback_ready: %s\n", snd_strerror (err));
      return pfds[1].revents & POLLOUT;
   }
   //cerr << (revents & POLLERR) << endl;
   return revents & POLLOUT;
}
Beispiel #10
0
int alsa_play_ready(struct alsa_dev *dev, struct pollfd *pfds,
		    unsigned int nfds)
{
	int ret;
	unsigned short revents = 0;
	ret = snd_pcm_poll_descriptors_revents(dev->playback_handle,
					       pfds + dev->read_fds,
					       dev->write_fds, &revents);
	if (ret < 0) {
		syslog_whine("error in alsa_play_ready: %s\n",
			     snd_strerror(ret));
		return pfds[1].revents & POLLOUT;
	}

	return revents & POLLOUT;
}
Beispiel #11
0
int alsa_cap_ready(struct alsa_dev *dev, struct pollfd *pfds,
		   unsigned int nfds)
{
	int ret;
	unsigned short revents = 0;

	ret = snd_pcm_poll_descriptors_revents(dev->capture_handle, pfds,
					       dev->read_fds, &revents);
	if (ret < 0) {
		syslog_whine("error in alsa_cap_ready: %s\n",
			     snd_strerror(ret));
		return pfds[0].revents & POLLIN;
	}

	return revents & POLLIN;
}
Beispiel #12
0
static void Poll (snd_pcm_t *pcm, int canc)
{
    int n = snd_pcm_poll_descriptors_count (pcm);
    struct pollfd ufd[n];
    unsigned short revents;

    snd_pcm_poll_descriptors (pcm, ufd, n);
    do
    {
        vlc_restorecancel (canc);
        while (poll (ufd, n, -1) == -1);
        canc = vlc_savecancel ();
        snd_pcm_poll_descriptors_revents (pcm, ufd, n, &revents);
    }
    while (!revents);
}
Beispiel #13
0
static enum stream_state
alsa_refill_stream(cubeb_stream * stm)
{
  int r;
  unsigned short revents;
  snd_pcm_sframes_t avail;
  long got;
  void * p;
  int draining;

  draining = 0;

  pthread_mutex_lock(&stm->mutex);

  r = snd_pcm_poll_descriptors_revents(stm->pcm, stm->fds, stm->nfds, &revents);
  if (r < 0 || revents != POLLOUT) {
    /* This should be a stream error; it makes no sense for poll(2) to wake
       for this stream and then have the stream report that it's not ready.
       Unfortunately, this does happen, so just bail out and try again. */
    pthread_mutex_unlock(&stm->mutex);
    return RUNNING;
  }

  avail = snd_pcm_avail_update(stm->pcm);
  if (avail == -EPIPE) {
    snd_pcm_recover(stm->pcm, avail, 1);
    avail = snd_pcm_avail_update(stm->pcm);
  }

  /* Failed to recover from an xrun, this stream must be broken. */
  if (avail < 0) {
    pthread_mutex_unlock(&stm->mutex);
    stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
    return ERROR;
  }

  /* This should never happen. */
  if ((unsigned int) avail > stm->buffer_size) {
    avail = stm->buffer_size;
  }

  /* poll(2) claims this stream is active, so there should be some space
     available to write.  If avail is still zero here, the stream must be in
     a funky state, so recover and try again. */
  if (avail == 0) {
    snd_pcm_recover(stm->pcm, -EPIPE, 1);
    avail = snd_pcm_avail_update(stm->pcm);
    if (avail <= 0) {
      pthread_mutex_unlock(&stm->mutex);
      stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
      return ERROR;
    }
  }

  p = calloc(1, snd_pcm_frames_to_bytes(stm->pcm, avail));
  assert(p);

  pthread_mutex_unlock(&stm->mutex);
  got = stm->data_callback(stm, stm->user_ptr, p, avail);
  pthread_mutex_lock(&stm->mutex);
  if (got < 0) {
    pthread_mutex_unlock(&stm->mutex);
    stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
    return ERROR;
  }
  if (got > 0) {
    snd_pcm_sframes_t wrote = snd_pcm_writei(stm->pcm, p, got);
    if (wrote == -EPIPE) {
      snd_pcm_recover(stm->pcm, wrote, 1);
      wrote = snd_pcm_writei(stm->pcm, p, got);
    }
    assert(wrote >= 0 && wrote == got);
    stm->write_position += wrote;
    gettimeofday(&stm->last_activity, NULL);
  }
  if (got != avail) {
    long buffer_fill = stm->buffer_size - (avail - got);
    double buffer_time = (double) buffer_fill / stm->params.rate;

    /* Fill the remaining buffer with silence to guarantee one full period
       has been written. */
    snd_pcm_writei(stm->pcm, (char *) p + got, avail - got);

    set_timeout(&stm->drain_timeout, buffer_time * 1000);

    draining = 1;
  }

  free(p);
  pthread_mutex_unlock(&stm->mutex);
  return draining ? DRAINING : RUNNING;
}
int main(int argc, char *argv[]) {
    const char *dev;
    int r, cap, count = 0;
    snd_pcm_hw_params_t *hwparams;
    snd_pcm_sw_params_t *swparams;
    snd_pcm_status_t *status;
    snd_pcm_t *pcm;
    unsigned rate = 44100;
    unsigned periods = 2;
    snd_pcm_uframes_t boundary, buffer_size = 44100/10; /* 100s */
    int dir = 1;
    struct timespec start, last_timestamp = { 0, 0 };
    uint64_t start_us, last_us = 0;
    snd_pcm_sframes_t last_avail = 0, last_delay = 0;
    struct pollfd *pollfds;
    int n_pollfd;
    int64_t sample_count = 0;
    struct sched_param sp;

    r = -1;
#ifdef _POSIX_PRIORITY_SCHEDULING
    sp.sched_priority = 5;
    r = pthread_setschedparam(pthread_self(), SCHED_RR, &sp);
#endif
    if (r)
        printf("Could not get RT prio. :(\n");

    snd_pcm_hw_params_alloca(&hwparams);
    snd_pcm_sw_params_alloca(&swparams);
    snd_pcm_status_alloca(&status);

    r = clock_gettime(CLOCK_MONOTONIC, &start);
    assert(r == 0);

    start_us = timespec_us(&start);

    dev = argc > 1 ? argv[1] : "front:AudioPCI";
    cap = argc > 2 ? atoi(argv[2]) : 0;

    if (cap == 0)
      r = snd_pcm_open(&pcm, dev, SND_PCM_STREAM_PLAYBACK, 0);
    else
      r = snd_pcm_open(&pcm, dev, SND_PCM_STREAM_CAPTURE, 0);
    assert(r == 0);

    r = snd_pcm_hw_params_any(pcm, hwparams);
    assert(r == 0);

    r = snd_pcm_hw_params_set_rate_resample(pcm, hwparams, 0);
    assert(r == 0);

    r = snd_pcm_hw_params_set_access(pcm, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED);
    assert(r == 0);

    r = snd_pcm_hw_params_set_format(pcm, hwparams, SND_PCM_FORMAT_S16_LE);
    assert(r == 0);

    r = snd_pcm_hw_params_set_rate_near(pcm, hwparams, &rate, NULL);
    assert(r == 0);

    r = snd_pcm_hw_params_set_channels(pcm, hwparams, 2);
    assert(r == 0);

    r = snd_pcm_hw_params_set_periods_integer(pcm, hwparams);
    assert(r == 0);

    r = snd_pcm_hw_params_set_periods_near(pcm, hwparams, &periods, &dir);
    assert(r == 0);

    r = snd_pcm_hw_params_set_buffer_size_near(pcm, hwparams, &buffer_size);
    assert(r == 0);

    r = snd_pcm_hw_params(pcm, hwparams);
    assert(r == 0);

    r = snd_pcm_hw_params_current(pcm, hwparams);
    assert(r == 0);

    r = snd_pcm_sw_params_current(pcm, swparams);
    assert(r == 0);

    if (cap == 0)
      r = snd_pcm_sw_params_set_avail_min(pcm, swparams, 1);
    else
      r = snd_pcm_sw_params_set_avail_min(pcm, swparams, 0);
    assert(r == 0);

    r = snd_pcm_sw_params_set_period_event(pcm, swparams, 0);
    assert(r == 0);

    r = snd_pcm_hw_params_get_buffer_size(hwparams, &buffer_size);
    assert(r == 0);
    r = snd_pcm_sw_params_set_start_threshold(pcm, swparams, buffer_size);
    assert(r == 0);

    r = snd_pcm_sw_params_get_boundary(swparams, &boundary);
    assert(r == 0);
    r = snd_pcm_sw_params_set_stop_threshold(pcm, swparams, boundary);
    assert(r == 0);

    r = snd_pcm_sw_params_set_tstamp_mode(pcm, swparams, SND_PCM_TSTAMP_ENABLE);
    assert(r == 0);

    r = snd_pcm_sw_params(pcm, swparams);
    assert(r == 0);

    r = snd_pcm_prepare(pcm);
    assert(r == 0);

    r = snd_pcm_sw_params_current(pcm, swparams);
    assert(r == 0);

/*     assert(snd_pcm_hw_params_is_monotonic(hwparams) > 0); */

    n_pollfd = snd_pcm_poll_descriptors_count(pcm);
    assert(n_pollfd > 0);

    pollfds = malloc(sizeof(struct pollfd) * n_pollfd);
    assert(pollfds);

    r = snd_pcm_poll_descriptors(pcm, pollfds, n_pollfd);
    assert(r == n_pollfd);

    printf("Starting. Buffer size is %u frames\n", (unsigned int) buffer_size);

    if (cap) {
      r = snd_pcm_start(pcm);
      assert(r == 0);
    }

    for (;;) {
        snd_pcm_sframes_t avail, delay;
        struct timespec now, timestamp;
        unsigned short revents;
        int handled = 0;
        uint64_t now_us, timestamp_us;
        snd_pcm_state_t state;
        unsigned long long pos;

        r = poll(pollfds, n_pollfd, 0);
        assert(r >= 0);

        r = snd_pcm_poll_descriptors_revents(pcm, pollfds, n_pollfd, &revents);
        assert(r == 0);

        if (cap == 0)
          assert((revents & ~POLLOUT) == 0);
        else
          assert((revents & ~POLLIN) == 0);

        avail = snd_pcm_avail(pcm);
        assert(avail >= 0);

        r = snd_pcm_status(pcm, status);
        assert(r == 0);

        /* This assertion fails from time to time. ALSA seems to be broken */
/*         assert(avail == (snd_pcm_sframes_t) snd_pcm_status_get_avail(status)); */
/*         printf("%lu %lu\n", (unsigned long) avail, (unsigned long) snd_pcm_status_get_avail(status)); */

        snd_pcm_status_get_htstamp(status, &timestamp);
        delay = snd_pcm_status_get_delay(status);
        state = snd_pcm_status_get_state(status);

        r = clock_gettime(CLOCK_MONOTONIC, &now);
        assert(r == 0);

        assert(!revents || avail > 0);

        if ((!cap && avail) || (cap && (unsigned)avail >= buffer_size)) {
            snd_pcm_sframes_t sframes;
            static const uint16_t psamples[2] = { 0, 0 };
            uint16_t csamples[2];

            if (cap == 0)
              sframes = snd_pcm_writei(pcm, psamples, 1);
            else
              sframes = snd_pcm_readi(pcm, csamples, 1);
            assert(sframes == 1);

            handled = 1;
            sample_count++;
        }

        if (!handled &&
            memcmp(&timestamp, &last_timestamp, sizeof(timestamp)) == 0 &&
            avail == last_avail &&
            delay == last_delay) {
            /* This is boring */
            continue;
        }

        now_us = timespec_us(&now);
        timestamp_us = timespec_us(&timestamp);

        if (cap == 0)
            pos = (unsigned long long) ((sample_count - handled - delay) * 1000000LU / 44100);
        else
            pos = (unsigned long long) ((sample_count - handled + delay) * 1000000LU / 44100);

        if (count++ % 50 == 0)
            printf("Elapsed\tCPU\tALSA\tPos\tSamples\tavail\tdelay\trevents\thandled\tstate\n");

        printf("%llu\t%llu\t%llu\t%llu\t%llu\t%li\t%li\t%i\t%i\t%i\n",
               (unsigned long long) (now_us - last_us),
               (unsigned long long) (now_us - start_us),
               (unsigned long long) (timestamp_us ? timestamp_us - start_us : 0),
               pos,
               (unsigned long long) sample_count,
               (signed long) avail,
               (signed long) delay,
               revents,
               handled,
               state);

        if (cap == 0)
          /** When this assert is hit, most likely something bad
           * happened, i.e. the avail jumped suddenly. */
          assert((unsigned) avail <= buffer_size);

        last_avail = avail;
        last_delay = delay;
        last_timestamp = timestamp;
        last_us = now_us;
    }

    return 0;
}
Beispiel #15
0
int snd_pcm_generic_poll_revents(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents)
{
    snd_pcm_generic_t *generic = pcm->private_data;
    return snd_pcm_poll_descriptors_revents(generic->slave, pfds, nfds, revents);
}
Beispiel #16
0
/*****************************************************************************
 * ALSAFill: function used to fill the ALSA buffer as much as possible
 *****************************************************************************/
static void ALSAFill( aout_instance_t * p_aout )
{
    struct aout_sys_t * p_sys = p_aout->output.p_sys;
    snd_pcm_t *p_pcm = p_sys->p_snd_pcm;
    snd_pcm_status_t * p_status;
    int i_snd_rc;
    mtime_t next_date;

    int canc = vlc_savecancel();
    /* Fill in the buffer until space or audio output buffer shortage */

    /* Get the status */
    snd_pcm_status_alloca(&p_status);
    i_snd_rc = snd_pcm_status( p_pcm, p_status );
    if( i_snd_rc < 0 )
    {
        msg_Err( p_aout, "cannot get device status" );
        goto error;
    }

    /* Handle buffer underruns and get the status again */
    if( snd_pcm_status_get_state( p_status ) == SND_PCM_STATE_XRUN )
    {
        /* Prepare the device */
        i_snd_rc = snd_pcm_prepare( p_pcm );
        if( i_snd_rc )
        {
            msg_Err( p_aout, "cannot recover from buffer underrun" );
            goto error;
        }

        msg_Dbg( p_aout, "recovered from buffer underrun" );

        /* Get the new status */
        i_snd_rc = snd_pcm_status( p_pcm, p_status );
        if( i_snd_rc < 0 )
        {
            msg_Err( p_aout, "cannot get device status after recovery" );
            goto error;
        }

        /* Underrun, try to recover as quickly as possible */
        next_date = mdate();
    }
    else
    {
        /* Here the device should be in RUNNING state, p_status is valid. */
        snd_pcm_sframes_t delay = snd_pcm_status_get_delay( p_status );
        if( delay == 0 ) /* workaround buggy alsa drivers */
            if( snd_pcm_delay( p_pcm, &delay ) < 0 )
                delay = 0; /* FIXME: use a positive minimal delay */

        size_t i_bytes = snd_pcm_frames_to_bytes( p_pcm, delay );
        mtime_t delay_us = CLOCK_FREQ * i_bytes
                / p_aout->output.output.i_bytes_per_frame
                / p_aout->output.output.i_rate
                * p_aout->output.output.i_frame_length;

#ifdef ALSA_DEBUG
        snd_pcm_state_t state = snd_pcm_status_get_state( p_status );
        if( state != SND_PCM_STATE_RUNNING )
            msg_Err( p_aout, "pcm status (%d) != RUNNING", state );

        msg_Dbg( p_aout, "Delay is %ld frames (%zu bytes)", delay, i_bytes );

        msg_Dbg( p_aout, "Bytes per frame: %d", p_aout->output.output.i_bytes_per_frame );
        msg_Dbg( p_aout, "Rate: %d", p_aout->output.output.i_rate );
        msg_Dbg( p_aout, "Frame length: %d", p_aout->output.output.i_frame_length );
        msg_Dbg( p_aout, "Next date: in %"PRId64" microseconds", delay_us );
#endif
        next_date = mdate() + delay_us;
    }

    block_t *p_buffer = aout_OutputNextBuffer( p_aout, next_date,
           (p_aout->output.output.i_format ==  VLC_CODEC_SPDIFL) );

    /* Audio output buffer shortage -> stop the fill process and wait */
    if( p_buffer == NULL )
        goto error;

    for (;;)
    {
        int n = snd_pcm_poll_descriptors_count(p_pcm);
        struct pollfd ufd[n];
        unsigned short revents;

        snd_pcm_poll_descriptors(p_pcm, ufd, n);
        do
        {
            vlc_restorecancel(canc);
            poll(ufd, n, -1);
            canc = vlc_savecancel();
            snd_pcm_poll_descriptors_revents(p_pcm, ufd, n, &revents);
        }
        while(!revents);

        if(revents & POLLOUT)
        {
            i_snd_rc = snd_pcm_writei( p_pcm, p_buffer->p_buffer,
                                       p_buffer->i_nb_samples );
            if( i_snd_rc != -ESTRPIPE )
                break;
        }

        /* a suspend event occurred
         * (stream is suspended and waiting for an application recovery) */
        msg_Dbg( p_aout, "entering in suspend mode, trying to resume..." );

        while( ( i_snd_rc = snd_pcm_resume( p_pcm ) ) == -EAGAIN )
        {
            vlc_restorecancel(canc);
            msleep(CLOCK_FREQ); /* device still suspended, wait... */
            canc = vlc_savecancel();
        }

        if( i_snd_rc < 0 )
            /* Device does not support resuming, restart it */
            i_snd_rc = snd_pcm_prepare( p_pcm );

    }

    if( i_snd_rc < 0 )
        msg_Err( p_aout, "cannot write: %s", snd_strerror( i_snd_rc ) );

    vlc_restorecancel(canc);
    block_Release( p_buffer );
    return;

error:
    if( i_snd_rc < 0 )
        msg_Err( p_aout, "ALSA error: %s", snd_strerror( i_snd_rc ) );

    vlc_restorecancel(canc);
    msleep(p_sys->i_period_time / 2);
}
Beispiel #17
0
static void *
cubeb_run_thread(void * context)
{
  cubeb * ctx = context;

  for (;;) {
    int r;
    struct cubeb_list_item * item;
    struct pollfd * tmppfds;
    struct cubeb_msg msg;
    cubeb_stream * stm;

    do {
      r = poll(ctx->descriptors, ctx->n_descriptors, -1);
    } while (r == -1 && errno == EINTR);
    assert(r > 0);

    /* XXX must handle ctrl messages last to maintain stream list to pfd mapping */
    /* XXX plus rebuild loses revents state anyway */

    item = ctx->active_streams;
    tmppfds = ctx->descriptors;
    while (item) {
      unsigned short revents;
      stm = item->data;

      r = snd_pcm_poll_descriptors_revents(stm->pcm, tmppfds, stm->n_descriptors, &revents);
      assert(r >= 0);

      if (revents & POLLERR) {
        /* XXX deal with this properly */
        assert(0);
      }

      if (revents & POLLOUT) {
        long got;
        snd_pcm_sframes_t avail = snd_pcm_avail_update(stm->pcm);
        void * p;
        if (avail == -EPIPE) {
          snd_pcm_recover(stm->pcm, avail, 1);
          avail = snd_pcm_avail_update(stm->pcm);
        }
        p = calloc(1, snd_pcm_frames_to_bytes(stm->pcm, avail));
        assert(p);
        got = stm->data_callback(stm, stm->user_ptr, p, avail);
        if (got < 0) {
          assert(0); /* XXX handle this case */
        }
        if (got > 0) {
          snd_pcm_sframes_t wrote = snd_pcm_writei(stm->pcm, p, got);
          stm->write_position += wrote;
        }
        if (got != avail) {
          struct cubeb_msg msg;
#if 0
          snd_pcm_state_t state = snd_pcm_state(stm->pcm);
          r = snd_pcm_drain(stm->pcm);
          assert(r == 0 || r == -EAGAIN);
#endif

          /* XXX write out a period of data to ensure real data is flushed to speakers */

          /* XXX only fire this once */
          stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);

#if 1
          /* XXX can't rebuild pfds until we've finished processing the current list */
          stm->state = CUBEB_STREAM_STATE_DEACTIVATING;

          msg.type = CUBEB_MSG_DEL_STREAM;
          msg.data = stm;
          cubeb_send_msg(stm->context, &msg);
#else
          /* disable fds for poll */
          /* XXX this will be undone upon next rebuild, so need to flag this somehow */
          for (i = 0; i < stm->n_descriptors; ++i) {
            tmppfds[i].fd = -1;
          }
#endif
        }
        free(p);
      }

      tmppfds += stm->n_descriptors;
      item = item->next;
    }

    if (tmppfds->revents & POLLIN) {
      cubeb_recv_msg(ctx, &msg);

      switch (msg.type) {
      case CUBEB_MSG_SHUTDOWN:
        return NULL;
      case CUBEB_MSG_ADD_STREAM:
        stm = msg.data;
        cubeb_register_active_stream(ctx, stm);
        pthread_mutex_lock(&stm->lock);
        assert(stm->state == CUBEB_STREAM_STATE_ACTIVATING);
        stm->state = CUBEB_STREAM_STATE_ACTIVE;
        pthread_cond_broadcast(&stm->cond);
        pthread_mutex_unlock(&stm->lock);
        break;
      case CUBEB_MSG_DEL_STREAM:
        stm = msg.data;
        cubeb_unregister_active_stream(ctx, stm);
        pthread_mutex_lock(&stm->lock);
        assert(stm->state == CUBEB_STREAM_STATE_DEACTIVATING);
        stm->state = CUBEB_STREAM_STATE_INACTIVE;
        pthread_cond_broadcast(&stm->cond);
        pthread_mutex_unlock(&stm->lock);
        break;
      }
    }
  }

  return NULL;
}
static
void *
audio_thread(void *param)
{
    struct pollfd  *fds = NULL;
    nfds_t          nfds = 0;
    static char     buf[16 * 1024];

    ppb_message_loop_mark_thread_unsuitable();

    nfds = do_rebuild_fds(&fds);
    pthread_barrier_wait(&stream_list_update_barrier);
    if (nfds == 0)
        goto quit;

    while (1) {
        if (g_atomic_int_get(&terminate_thread))
            goto quit;

        int res = poll(fds, nfds, 10 * 1000);
        if (res == -1) {
            if (errno == EINTR)
                continue;
            trace_error("%s, poll, errno=%d\n", __func__, errno);
            continue;
        }

        if (res == 0 || fds == NULL)
            continue;

        if (fds[0].revents)
            drain_wakeup_pipe(fds[0].fd);

        if (g_atomic_int_get(&rebuild_fds)) {
            nfds = do_rebuild_fds(&fds);
            pthread_barrier_wait(&stream_list_update_barrier);
            if (nfds == 0)
                goto quit;
        }

        for (uintptr_t k = 1; k < nfds; k ++) {
            unsigned short revents = 0;
            audio_stream *as = g_hash_table_lookup(stream_by_fd_ht, GINT_TO_POINTER(fds[k].fd));

            // check if stream was deleted
            if (!as)
                continue;

            snd_pcm_poll_descriptors_revents(as->pcm, &fds[k], 1, &revents);

            if (revents & (~(POLLIN | POLLOUT))) {
                trace_warning("%s, revents have unexpected flags set (%u)\n", __func__,
                              (unsigned int)revents);
                recover_pcm(as->pcm);
            }

            if (revents & (POLLIN | POLLOUT)) {
                int                 paused = g_atomic_int_get(&as->paused);
                snd_pcm_sframes_t   frame_count = snd_pcm_avail(as->pcm);

                if (revents & POLLIN) {
                    // POLLIN
                    const size_t frame_size = 1 * sizeof(int16_t); // mono 16-bit
                    const size_t max_segment_length = MIN(as->sample_frame_count * frame_size,
                                                          sizeof(buf));
                    size_t       to_process = frame_count * frame_size;

                    while (to_process > 0) {
                        snd_pcm_sframes_t frames_read;
                        const size_t segment_length = MIN(to_process, max_segment_length);

                        frames_read = snd_pcm_readi(as->pcm, buf, segment_length / frame_size);
                        if (frames_read < 0) {
                            trace_warning("%s, snd_pcm_readi error %d\n", __func__,
                                          (int)frames_read);
                            recover_pcm(as->pcm);
                            continue;
                        }

                        if (!paused && as->capture_cb)
                            as->capture_cb(buf, frames_read * frame_size, 0, as->cb_user_data);

                        to_process -= frames_read * frame_size;
                    }

                } else {
                    // POLLOUT
                    const size_t frame_size = 2 * sizeof(int16_t); // stereo 16-bit
                    const size_t max_segment_length = MIN(as->sample_frame_count * frame_size,
                                                          sizeof(buf));
                    size_t       to_process = frame_count * frame_size;

                    while (to_process > 0) {
                        snd_pcm_sframes_t frames_written;
                        const size_t segment_length = MIN(to_process, max_segment_length);

                        if (paused || !as->playback_cb)
                            memset(buf, 0, segment_length);
                        else
                            as->playback_cb(buf, segment_length, 0, as->cb_user_data);

                        frames_written = snd_pcm_writei(as->pcm, buf, segment_length / frame_size);

                        if (frames_written < 0) {
                            trace_warning("%s, snd_pcm_writei error %d\n", __func__,
                                          (int)frames_written);
                            recover_pcm(as->pcm);
                            continue;
                        }

                        to_process -= frames_written * frame_size;
                    }
                }
            }
        }
    }

quit:
    free(fds);
    return NULL;
}
Beispiel #19
0
static int wait( PaAlsaStream *stream )
{
    int need_capture;
    int need_playback;
    int capture_avail = INT_MAX;
    int playback_avail = INT_MAX;
    int common_avail;

    if( stream->pcm_capture )
        need_capture = 1;
    else
        need_capture = 0;

    if( stream->pcm_playback )
        need_playback = 1;
    else
        need_playback = 0;

    while( need_capture || need_playback )
    {
        int playback_pfd_offset;
        int total_fds = 0;

        /* if the main thread has requested that we stop, do so now */
        pthread_testcancel();

        /*PA_DEBUG(("still polling...\n"));
        if( need_capture )
            PA_DEBUG(("need capture.\n"));
        if( need_playback )
            PA_DEBUG(("need playback.\n")); */

        /* get the fds, packing all applicable fds into a single array,
         * so we can check them all with a single poll() call */

        if( need_capture )
        {
            snd_pcm_poll_descriptors( stream->pcm_capture, stream->pfds,
                                      stream->capture_nfds );
            total_fds += stream->capture_nfds;
        }

        if( need_playback )
        {
            playback_pfd_offset = total_fds;
            snd_pcm_poll_descriptors( stream->pcm_playback,
                                      stream->pfds + playback_pfd_offset,
                                      stream->playback_nfds );
            total_fds += stream->playback_nfds;
        }

        /* now poll on the combination of playback and capture fds.
         * TODO: handle interrupt and/or failure */
        poll( stream->pfds, total_fds, 1000 );

        /* check the return status of our pfds */
        if( need_capture )
        {
            short revents;
            snd_pcm_poll_descriptors_revents( stream->pcm_capture, stream->pfds,
                                              stream->capture_nfds, &revents );
            if( revents == POLLIN )
                need_capture = 0;
        }

        if( need_playback )
        {
            short revents;
            snd_pcm_poll_descriptors_revents( stream->pcm_playback,
                                              stream->pfds + playback_pfd_offset,
                                              stream->playback_nfds, &revents );
            //if( revents & POLLOUT )
            //if( revents & POLLERR )
            //    PA_DEBUG(("polling error!"));
            if( revents == POLLOUT )
                need_playback = 0;
        }
    }

    /* we have now established that there are buffers ready to be
     * operated on.  Now determine how many frames are available. */
    if( stream->pcm_capture )
        capture_avail = snd_pcm_avail_update( stream->pcm_capture );

    if( stream->pcm_playback )
        playback_avail = snd_pcm_avail_update( stream->pcm_playback );

    common_avail = MIN(capture_avail, playback_avail);
    common_avail -= common_avail % stream->frames_per_period;

    return common_avail;
}