Exemple #1
0
/*
 * Loop to skip the first few samples of a stream
 */
static int pulse_skip(struct pulse_data *data)
{
	uint64_t skip = 1;
	const void *frames;
	size_t bytes;
	uint64_t pa_time;

	while (os_event_try(data->event) == EAGAIN) {
		pulse_iterate(data);
		pa_stream_peek(data->stream, &frames, &bytes);

		if (!bytes)
			continue;
		if (!frames || pa_stream_get_time(data->stream, &pa_time) < 0) {
			pa_stream_drop(data->stream);
			continue;
		}

		if (skip == 1 && pa_time)
			skip = pa_time;
		if (skip + pulse_start_delay < pa_time)
			return 0;

		pa_stream_drop(data->stream);
	}

	return -1;
}
int64_t PulseAudioPlayer::GetCurrentPosition()
{
	if (!is_playing) return 0;

	// FIXME: this should be based on not duration played but actual sample being heard
	// (during vidoeo playback, cur_frame might get changed to resync)

	// Calculation duration we have played, in microseconds
	pa_usec_t play_cur_time;
	pa_stream_get_time(stream, &play_cur_time);
	pa_usec_t playtime = play_cur_time - play_start_time;

	return start_frame + playtime * provider->GetSampleRate() / (1000*1000);
}
Exemple #3
0
/*
 * Callback for pulse which gets executed when new audio data is available
 */
static void pulse_stream_read(pa_stream *p, size_t nbytes, void *userdata)
{
	UNUSED_PARAMETER(p);
	UNUSED_PARAMETER(nbytes);
	PULSE_DATA(userdata);

	const void *frames;
	size_t bytes;
	uint64_t pa_time;
	int64_t pa_latency;

	pa_stream_peek(data->stream, &frames, &bytes);

	// check if we got data
	if (!bytes)
		goto exit;

	if (!frames) {
		blog(LOG_DEBUG,
			"pulse-input: Got audio hole of %u bytes",
			(unsigned int) bytes);
		pa_stream_drop(data->stream);
		goto exit;
	}

	if (pa_stream_get_time(data->stream, &pa_time) < 0) {
		blog(LOG_ERROR,
			"pulse-input: Failed to get timing info !");
		pa_stream_drop(data->stream);
		goto exit;
	}

	pulse_get_stream_latency(data->stream, &pa_latency);

	struct source_audio out;
	out.speakers        = data->speakers;
	out.samples_per_sec = data->samples_per_sec;
	out.format          = pulse_to_obs_audio_format(data->format);
	out.data[0]         = (uint8_t *) frames;
	out.frames          = bytes / data->bytes_per_frame;
	out.timestamp       = (pa_time - pa_latency) * 1000;
	obs_source_output_audio(data->source, &out);

	pa_stream_drop(data->stream);

exit:
	pulse_signal(0);
}
void PulseAudioPlayer::Play(int64_t start,int64_t count)
{
	//printf("Starting PulseAudio playback\n");
	if (!open) OpenStream();

	if (is_playing) {
		// If we're already playing, do a quick "reset"
		is_playing = false;

		pa_threaded_mainloop_lock(mainloop);
		pa_operation *op = pa_stream_flush(stream, (pa_stream_success_cb_t)pa_stream_success, this);
		pa_threaded_mainloop_unlock(mainloop);
		stream_success.Wait();
		pa_operation_unref(op);
		if (!stream_success_val) {
			paerror = pa_context_errno(context);
			printf("PulseAudio player: Error flushing stream: %s (%d)\n", pa_strerror(paerror), paerror);
		}
	}

	start_frame = start;
	cur_frame = start;
	end_frame = start + count;
	//printf("start=%lu end=%lu\n", start_frame, end_frame);

	is_playing = true;

	play_start_time = 0;
	pa_threaded_mainloop_lock(mainloop);
	paerror = pa_stream_get_time(stream, (pa_usec_t*) &play_start_time);
	pa_threaded_mainloop_unlock(mainloop);
	if (paerror) {
		printf("PulseAudio player: Error getting stream time: %s (%d)\n", pa_strerror(paerror), paerror);
	}

	PulseAudioPlayer::pa_stream_write(stream, pa_stream_writable_size(stream), this);

	pa_threaded_mainloop_lock(mainloop);
	pa_operation *op = pa_stream_trigger(stream, (pa_stream_success_cb_t)pa_stream_success, this);
	pa_threaded_mainloop_unlock(mainloop);
	stream_success.Wait();
	pa_operation_unref(op);
	if (!stream_success_val) {
		paerror = pa_context_errno(context);
		printf("PulseAudio player: Error triggering stream: %s (%d)\n", pa_strerror(paerror), paerror);
	}
}
Exemple #5
0
/* Show the current latency */
static void stream_update_timing_callback(pa_stream *s, int success, void *userdata) {
    pa_usec_t l, usec;
    int negative = 0;

    pa_assert(s);

    if (!success ||
        pa_stream_get_time(s, &usec) < 0 ||
        pa_stream_get_latency(s, &l, &negative) < 0) {
        pa_log(_("Failed to get latency: %s"), pa_strerror(pa_context_errno(context)));
        quit(1);
        return;
    }

    fprintf(stderr, _("Time: %0.3f sec; Latency: %0.0f usec."),
            (float) usec / 1000000,
            (float) l * (negative?-1.0f:1.0f));
    fprintf(stderr, "        \r");
}
/* Show the current latency */
void QPulseAudioThread::stream_update_timing_callback ( pa_stream *s, int success, void *userdata )
{
    pa_usec_t latency, usec;
    int negative = 0;

    assert ( s );

    if ( !success ||
            pa_stream_get_time ( s, &usec ) < 0 ||
            pa_stream_get_latency ( s, &latency, &negative ) < 0 ) {
        fprintf ( stderr, "Failed to get latency: %s\n", pa_strerror ( pa_context_errno ( context ) ) );
        pulseQuit ( 1 );
        return;
    }

    fprintf ( stderr, "Time: %0.3f sec; Latency: %0.0f usec.  \r",
              ( float ) usec / 1000000,
              ( float ) latency * ( negative?-1:1 ) );
}
int
sa_stream_get_position(sa_stream_t *s, sa_position_t position, int64_t *pos) {
  pa_usec_t usec;
  if (s == NULL || s->stream == NULL) {
    return SA_ERROR_NO_INIT;
  }
  if (position != SA_POSITION_WRITE_SOFTWARE) {
    return SA_ERROR_NOT_SUPPORTED;
  }
  pa_threaded_mainloop_lock(s->m);
  if(pa_stream_get_time(s->stream,  &usec) != PA_ERR_NODATA) {
    *pos = pa_usec_to_bytes(usec, &s->sample_spec);
  }
  else {
    *pos  = s->bytes_written;
  }
  pa_threaded_mainloop_unlock(s->m);
  return SA_SUCCESS;
}
Exemple #8
0
int
cubeb_stream_get_position(cubeb_stream * stm, uint64_t * position)
{
  int r;
  pa_usec_t r_usec;
  uint64_t bytes;

  pa_threaded_mainloop_lock(stm->context->mainloop);
  r = pa_stream_get_time(stm->stream, &r_usec);
  pa_threaded_mainloop_unlock(stm->context->mainloop);

  if (r != 0) {
    return CUBEB_ERROR;
  }

  /* XXX might be more accurate to compute directly from get_timing_info */
  bytes = pa_usec_to_bytes(r_usec, &stm->sample_spec);
  *position = bytes / pa_frame_size(&stm->sample_spec);

  return CUBEB_OK;
}
Exemple #9
0
static int Control(demux_t *demux, int query, va_list ap)
{
    demux_sys_t *sys = demux->p_sys;

    switch (query)
    {
        case DEMUX_GET_TIME:
        {
            pa_usec_t us;

            if (pa_stream_get_time(sys->stream, &us) < 0)
                return VLC_EGENERIC;
            *(va_arg(ap, int64_t *)) = us;
            break;
        }

        //case DEMUX_SET_NEXT_DEMUX_TIME: TODO
        //case DEMUX_GET_META TODO

        case DEMUX_GET_PTS_DELAY:
            *(va_arg(ap, int64_t *)) = sys->caching;
            break;

        case DEMUX_HAS_UNSUPPORTED_META:
        case DEMUX_CAN_RECORD:
        case DEMUX_CAN_PAUSE:
        case DEMUX_CAN_CONTROL_PACE:
        case DEMUX_CAN_CONTROL_RATE:
        case DEMUX_CAN_SEEK:
            *(va_arg(ap, bool *)) = false;
            break;

        default:
            return VLC_EGENERIC;
    }

    return VLC_SUCCESS;
}
Exemple #10
0
static GstClockTime
gst_pulsesrc_get_time (GstClock * clock, GstPulseSrc * src)
{
  pa_usec_t time = 0;

  pa_threaded_mainloop_lock (src->mainloop);

  if (gst_pulsesrc_is_dead (src, TRUE)) {
    goto unlock_and_out;
  }

  if (pa_stream_get_time (src->stream, &time) < 0) {
    GST_DEBUG_OBJECT (src, "could not get time");
    time = GST_CLOCK_TIME_NONE;
  } else {
    time *= 1000;
  }


unlock_and_out:
  pa_threaded_mainloop_unlock (src->mainloop);

  return time;
}
Exemple #11
0
/*
 * Worker thread to get audio data
 *
 * Will run until signaled
 */
static void *pulse_thread(void *vptr)
{
	PULSE_DATA(vptr);

	if (pulse_connect(data) < 0)
		return NULL;
	if (pulse_get_server_info(data) < 0)
		return NULL;
	if (pulse_connect_stream(data) < 0)
		return NULL;

	if (pulse_skip(data) < 0)
		return NULL;

	blog(LOG_DEBUG, "pulse-input: Start recording");

	const void *frames;
	size_t bytes;
	uint64_t pa_time;
	int64_t pa_latency;

	struct source_audio out;
	out.speakers = data->speakers;
	out.samples_per_sec = data->samples_per_sec;
	out.format = pulse_to_obs_audio_format(data->format);

	while (os_event_try(data->event) == EAGAIN) {
		pulse_iterate(data);

		pa_stream_peek(data->stream, &frames, &bytes);

		// check if we got data
		if (!bytes)
			continue;
		if (!frames) {
			blog(LOG_DEBUG,
				"pulse-input: Got audio hole of %u bytes",
				(unsigned int) bytes);
			pa_stream_drop(data->stream);
			continue;
		}

		if (pa_stream_get_time(data->stream, &pa_time) < 0) {
			blog(LOG_ERROR,
				"pulse-input: Failed to get timing info !");
			pa_stream_drop(data->stream);
			continue;
		}

		pulse_get_stream_latency(data->stream, &pa_latency);

		out.data[0] = (uint8_t *) frames;
		out.frames = frames_to_bytes(data, bytes);
		out.timestamp = (pa_time - pa_latency) * 1000;
		obs_source_output_audio(data->source, &out);

		pa_stream_drop(data->stream);
	}

	pulse_diconnect_stream(data);
	pulse_disconnect(data);

	return NULL;
}
/* This is called whenever the context status changes */
static void context_state_callback(pa_context *c, void *userdata) {
    fail_unless(c != NULL);

    switch (pa_context_get_state(c)) {
        case PA_CONTEXT_CONNECTING:
        case PA_CONTEXT_AUTHORIZING:
        case PA_CONTEXT_SETTING_NAME:
            break;

        case PA_CONTEXT_READY: {
            pa_stream_flags_t flags = PA_STREAM_AUTO_TIMING_UPDATE;
            pa_buffer_attr attr;
            static const pa_sample_spec ss = {
                .format = PA_SAMPLE_S16LE,
                .rate = 44100,
                .channels = 2
            };

            pa_zero(attr);
            attr.maxlength = (uint32_t) -1;
            attr.tlength = latency > 0 ? (uint32_t) pa_usec_to_bytes(latency, &ss) : (uint32_t) -1;
            attr.prebuf = (uint32_t) -1;
            attr.minreq = (uint32_t) -1;
            attr.fragsize = (uint32_t) -1;

#ifdef INTERPOLATE
            flags |= PA_STREAM_INTERPOLATE_TIMING;
#endif

            if (latency > 0)
                flags |= PA_STREAM_ADJUST_LATENCY;

            pa_log("Connection established");

            stream = pa_stream_new(c, "interpol-test", &ss, NULL);
            fail_unless(stream != NULL);

            if (playback) {
                pa_assert_se(pa_stream_connect_playback(stream, NULL, &attr, flags, NULL, NULL) == 0);
                pa_stream_set_write_callback(stream, stream_write_cb, NULL);
            } else {
                pa_assert_se(pa_stream_connect_record(stream, NULL, &attr, flags) == 0);
                pa_stream_set_read_callback(stream, stream_read_cb, NULL);
            }

            pa_stream_set_latency_update_callback(stream, stream_latency_cb, NULL);

            break;
        }

        case PA_CONTEXT_TERMINATED:
            break;

        case PA_CONTEXT_FAILED:
        default:
            pa_log_error("Context error: %s", pa_strerror(pa_context_errno(c)));
            ck_abort();
    }
}

START_TEST (interpol_test) {
    pa_threaded_mainloop* m = NULL;
    int k;
    struct timeval start, last_info = { 0, 0 };
    pa_usec_t old_t = 0, old_rtc = 0;
#ifdef CORK
    bool corked = false;
#endif

    /* Set up a new main loop */
    m = pa_threaded_mainloop_new();
    fail_unless(m != NULL);
    mainloop_api = pa_threaded_mainloop_get_api(m);
    fail_unless(mainloop_api != NULL);
    context = pa_context_new(mainloop_api, bname);
    fail_unless(context != NULL);

    pa_context_set_state_callback(context, context_state_callback, NULL);

    fail_unless(pa_context_connect(context, NULL, 0, NULL) >= 0);

    pa_gettimeofday(&start);

    fail_unless(pa_threaded_mainloop_start(m) >= 0);

/* #ifdef CORK */
    for (k = 0; k < 20000; k++)
/* #else */
/*     for (k = 0; k < 2000; k++) */
/* #endif */
    {
        bool success = false, changed = false;
        pa_usec_t t, rtc, d;
        struct timeval now, tv;
        bool playing = false;

        pa_threaded_mainloop_lock(m);

        if (stream) {
            const pa_timing_info *info;

            if (pa_stream_get_time(stream, &t) >= 0 &&
                pa_stream_get_latency(stream, &d, NULL) >= 0)
                success = true;

            if ((info = pa_stream_get_timing_info(stream))) {
                if (memcmp(&last_info, &info->timestamp, sizeof(struct timeval))) {
                    changed = true;
                    last_info = info->timestamp;
                }
                if (info->playing)
                    playing = true;
            }
        }

        pa_threaded_mainloop_unlock(m);

        pa_gettimeofday(&now);

        if (success) {
#ifdef CORK
            bool cork_now;
#endif
            rtc = pa_timeval_diff(&now, &start);
            pa_log_info("%i\t%llu\t%llu\t%llu\t%llu\t%lli\t%u\t%u\t%llu\t%llu\n", k,
                   (unsigned long long) rtc,
                   (unsigned long long) t,
                   (unsigned long long) (rtc-old_rtc),
                   (unsigned long long) (t-old_t),
                   (signed long long) rtc - (signed long long) t,
                   changed,
                   playing,
                   (unsigned long long) latency,
                   (unsigned long long) d);

            fflush(stdout);
            old_t = t;
            old_rtc = rtc;

#ifdef CORK
            cork_now = (rtc / (2*PA_USEC_PER_SEC)) % 2 == 1;

            if (corked != cork_now) {
                pa_threaded_mainloop_lock(m);
                pa_operation_unref(pa_stream_cork(stream, cork_now, NULL, NULL));
                pa_threaded_mainloop_unlock(m);

                pa_log(cork_now ? "Corking" : "Uncorking");

                corked = cork_now;
            }
#endif
        }

        /* Spin loop, ugly but normal usleep() is just too badly grained */
        tv = now;
        while (pa_timeval_diff(pa_gettimeofday(&now), &tv) < 1000)
            pa_thread_yield();
    }

    if (m)
        pa_threaded_mainloop_stop(m);

    if (stream) {
        pa_stream_disconnect(stream);
        pa_stream_unref(stream);
    }

    if (context) {
        pa_context_disconnect(context);
        pa_context_unref(context);
    }

    if (m)
        pa_threaded_mainloop_free(m);
}
END_TEST

int main(int argc, char *argv[]) {
    int failed = 0;
    Suite *s;
    TCase *tc;
    SRunner *sr;

    if (!getenv("MAKE_CHECK"))
        pa_log_set_level(PA_LOG_DEBUG);

    bname = argv[0];
    playback = argc <= 1 || !pa_streq(argv[1], "-r");
    latency = (argc >= 2 && !pa_streq(argv[1], "-r")) ? atoi(argv[1]) : (argc >= 3 ? atoi(argv[2]) : 0);

    s = suite_create("Interpol");
    tc = tcase_create("interpol");
    tcase_add_test(tc, interpol_test);
    tcase_set_timeout(tc, 5 * 60);
    suite_add_tcase(s, tc);

    sr = srunner_create(s);
    srunner_run_all(sr, CK_NORMAL);
    failed = srunner_ntests_failed(sr);
    srunner_free(sr);

    return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
}