153a5a1b3Sopenharmony_ci/***
253a5a1b3Sopenharmony_ci  This file is part of PulseAudio.
353a5a1b3Sopenharmony_ci
453a5a1b3Sopenharmony_ci  PulseAudio is free software; you can redistribute it and/or modify
553a5a1b3Sopenharmony_ci  it under the terms of the GNU Lesser General Public License as published
653a5a1b3Sopenharmony_ci  by the Free Software Foundation; either version 2.1 of the License,
753a5a1b3Sopenharmony_ci  or (at your option) any later version.
853a5a1b3Sopenharmony_ci
953a5a1b3Sopenharmony_ci  PulseAudio is distributed in the hope that it will be useful, but
1053a5a1b3Sopenharmony_ci  WITHOUT ANY WARRANTY; without even the implied warranty of
1153a5a1b3Sopenharmony_ci  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1253a5a1b3Sopenharmony_ci  General Public License for more details.
1353a5a1b3Sopenharmony_ci
1453a5a1b3Sopenharmony_ci  You should have received a copy of the GNU Lesser General Public License
1553a5a1b3Sopenharmony_ci  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
1653a5a1b3Sopenharmony_ci***/
1753a5a1b3Sopenharmony_ci
1853a5a1b3Sopenharmony_ci#ifdef HAVE_CONFIG_H
1953a5a1b3Sopenharmony_ci#include <config.h>
2053a5a1b3Sopenharmony_ci#endif
2153a5a1b3Sopenharmony_ci
2253a5a1b3Sopenharmony_ci#include <signal.h>
2353a5a1b3Sopenharmony_ci#include <string.h>
2453a5a1b3Sopenharmony_ci#include <errno.h>
2553a5a1b3Sopenharmony_ci#include <unistd.h>
2653a5a1b3Sopenharmony_ci#include <stdio.h>
2753a5a1b3Sopenharmony_ci#include <stdlib.h>
2853a5a1b3Sopenharmony_ci
2953a5a1b3Sopenharmony_ci#include <check.h>
3053a5a1b3Sopenharmony_ci
3153a5a1b3Sopenharmony_ci#include <pulse/pulseaudio.h>
3253a5a1b3Sopenharmony_ci#include <pulse/mainloop.h>
3353a5a1b3Sopenharmony_ci
3453a5a1b3Sopenharmony_ci#include <pulsecore/log.h>
3553a5a1b3Sopenharmony_ci#include <pulsecore/macro.h>
3653a5a1b3Sopenharmony_ci#include <pulsecore/thread.h>
3753a5a1b3Sopenharmony_ci
3853a5a1b3Sopenharmony_ci#define INTERPOLATE
3953a5a1b3Sopenharmony_ci//#define CORK
4053a5a1b3Sopenharmony_ci
4153a5a1b3Sopenharmony_cistatic pa_context *context = NULL;
4253a5a1b3Sopenharmony_cistatic pa_stream *stream = NULL;
4353a5a1b3Sopenharmony_cistatic pa_mainloop_api *mainloop_api = NULL;
4453a5a1b3Sopenharmony_cistatic bool playback = true;
4553a5a1b3Sopenharmony_cistatic pa_usec_t latency = 0;
4653a5a1b3Sopenharmony_cistatic const char *bname = NULL;
4753a5a1b3Sopenharmony_ci
4853a5a1b3Sopenharmony_cistatic void stream_write_cb(pa_stream *p, size_t nbytes, void *userdata) {
4953a5a1b3Sopenharmony_ci    /* Just some silence */
5053a5a1b3Sopenharmony_ci
5153a5a1b3Sopenharmony_ci    for (;;) {
5253a5a1b3Sopenharmony_ci        void *data;
5353a5a1b3Sopenharmony_ci
5453a5a1b3Sopenharmony_ci        fail_unless((nbytes = pa_stream_writable_size(p)) != (size_t) -1);
5553a5a1b3Sopenharmony_ci
5653a5a1b3Sopenharmony_ci        if (nbytes <= 0)
5753a5a1b3Sopenharmony_ci            break;
5853a5a1b3Sopenharmony_ci
5953a5a1b3Sopenharmony_ci        fail_unless(pa_stream_begin_write(p, &data, &nbytes) == 0);
6053a5a1b3Sopenharmony_ci        pa_memzero(data, nbytes);
6153a5a1b3Sopenharmony_ci        fail_unless(pa_stream_write(p, data, nbytes, NULL, 0, PA_SEEK_RELATIVE) == 0);
6253a5a1b3Sopenharmony_ci    }
6353a5a1b3Sopenharmony_ci}
6453a5a1b3Sopenharmony_ci
6553a5a1b3Sopenharmony_cistatic void stream_read_cb(pa_stream *p, size_t nbytes, void *userdata) {
6653a5a1b3Sopenharmony_ci    /* We don't care about the data, just drop it */
6753a5a1b3Sopenharmony_ci
6853a5a1b3Sopenharmony_ci    for (;;) {
6953a5a1b3Sopenharmony_ci        const void *data;
7053a5a1b3Sopenharmony_ci
7153a5a1b3Sopenharmony_ci        pa_assert_se((nbytes = pa_stream_readable_size(p)) != (size_t) -1);
7253a5a1b3Sopenharmony_ci
7353a5a1b3Sopenharmony_ci        if (nbytes <= 0)
7453a5a1b3Sopenharmony_ci            break;
7553a5a1b3Sopenharmony_ci
7653a5a1b3Sopenharmony_ci        fail_unless(pa_stream_peek(p, &data, &nbytes) == 0);
7753a5a1b3Sopenharmony_ci        fail_unless(pa_stream_drop(p) == 0);
7853a5a1b3Sopenharmony_ci    }
7953a5a1b3Sopenharmony_ci}
8053a5a1b3Sopenharmony_ci
8153a5a1b3Sopenharmony_cistatic void stream_latency_cb(pa_stream *p, void *userdata) {
8253a5a1b3Sopenharmony_ci#ifndef INTERPOLATE
8353a5a1b3Sopenharmony_ci    pa_operation *o;
8453a5a1b3Sopenharmony_ci
8553a5a1b3Sopenharmony_ci    o = pa_stream_update_timing_info(p, NULL, NULL);
8653a5a1b3Sopenharmony_ci    pa_operation_unref(o);
8753a5a1b3Sopenharmony_ci#endif
8853a5a1b3Sopenharmony_ci}
8953a5a1b3Sopenharmony_ci
9053a5a1b3Sopenharmony_ci/* This is called whenever the context status changes */
9153a5a1b3Sopenharmony_cistatic void context_state_callback(pa_context *c, void *userdata) {
9253a5a1b3Sopenharmony_ci    fail_unless(c != NULL);
9353a5a1b3Sopenharmony_ci
9453a5a1b3Sopenharmony_ci    switch (pa_context_get_state(c)) {
9553a5a1b3Sopenharmony_ci        case PA_CONTEXT_CONNECTING:
9653a5a1b3Sopenharmony_ci        case PA_CONTEXT_AUTHORIZING:
9753a5a1b3Sopenharmony_ci        case PA_CONTEXT_SETTING_NAME:
9853a5a1b3Sopenharmony_ci            break;
9953a5a1b3Sopenharmony_ci
10053a5a1b3Sopenharmony_ci        case PA_CONTEXT_READY: {
10153a5a1b3Sopenharmony_ci            pa_stream_flags_t flags = PA_STREAM_AUTO_TIMING_UPDATE;
10253a5a1b3Sopenharmony_ci            pa_buffer_attr attr;
10353a5a1b3Sopenharmony_ci            static const pa_sample_spec ss = {
10453a5a1b3Sopenharmony_ci                .format = PA_SAMPLE_S16LE,
10553a5a1b3Sopenharmony_ci                .rate = 44100,
10653a5a1b3Sopenharmony_ci                .channels = 2
10753a5a1b3Sopenharmony_ci            };
10853a5a1b3Sopenharmony_ci
10953a5a1b3Sopenharmony_ci            pa_zero(attr);
11053a5a1b3Sopenharmony_ci            attr.maxlength = (uint32_t) -1;
11153a5a1b3Sopenharmony_ci            attr.tlength = latency > 0 ? (uint32_t) pa_usec_to_bytes(latency, &ss) : (uint32_t) -1;
11253a5a1b3Sopenharmony_ci            attr.prebuf = (uint32_t) -1;
11353a5a1b3Sopenharmony_ci            attr.minreq = (uint32_t) -1;
11453a5a1b3Sopenharmony_ci            attr.fragsize = (uint32_t) -1;
11553a5a1b3Sopenharmony_ci
11653a5a1b3Sopenharmony_ci#ifdef INTERPOLATE
11753a5a1b3Sopenharmony_ci            flags |= PA_STREAM_INTERPOLATE_TIMING;
11853a5a1b3Sopenharmony_ci#endif
11953a5a1b3Sopenharmony_ci
12053a5a1b3Sopenharmony_ci            if (latency > 0)
12153a5a1b3Sopenharmony_ci                flags |= PA_STREAM_ADJUST_LATENCY;
12253a5a1b3Sopenharmony_ci
12353a5a1b3Sopenharmony_ci            pa_log("Connection established");
12453a5a1b3Sopenharmony_ci
12553a5a1b3Sopenharmony_ci            stream = pa_stream_new(c, "interpol-test", &ss, NULL);
12653a5a1b3Sopenharmony_ci            fail_unless(stream != NULL);
12753a5a1b3Sopenharmony_ci
12853a5a1b3Sopenharmony_ci            if (playback) {
12953a5a1b3Sopenharmony_ci                pa_assert_se(pa_stream_connect_playback(stream, NULL, &attr, flags, NULL, NULL) == 0);
13053a5a1b3Sopenharmony_ci                pa_stream_set_write_callback(stream, stream_write_cb, NULL);
13153a5a1b3Sopenharmony_ci            } else {
13253a5a1b3Sopenharmony_ci                pa_assert_se(pa_stream_connect_record(stream, NULL, &attr, flags) == 0);
13353a5a1b3Sopenharmony_ci                pa_stream_set_read_callback(stream, stream_read_cb, NULL);
13453a5a1b3Sopenharmony_ci            }
13553a5a1b3Sopenharmony_ci
13653a5a1b3Sopenharmony_ci            pa_stream_set_latency_update_callback(stream, stream_latency_cb, NULL);
13753a5a1b3Sopenharmony_ci
13853a5a1b3Sopenharmony_ci            break;
13953a5a1b3Sopenharmony_ci        }
14053a5a1b3Sopenharmony_ci
14153a5a1b3Sopenharmony_ci        case PA_CONTEXT_TERMINATED:
14253a5a1b3Sopenharmony_ci            break;
14353a5a1b3Sopenharmony_ci
14453a5a1b3Sopenharmony_ci        case PA_CONTEXT_FAILED:
14553a5a1b3Sopenharmony_ci        default:
14653a5a1b3Sopenharmony_ci            pa_log_error("Context error: %s", pa_strerror(pa_context_errno(c)));
14753a5a1b3Sopenharmony_ci            ck_abort();
14853a5a1b3Sopenharmony_ci    }
14953a5a1b3Sopenharmony_ci}
15053a5a1b3Sopenharmony_ci
15153a5a1b3Sopenharmony_ciSTART_TEST (interpol_test) {
15253a5a1b3Sopenharmony_ci    pa_threaded_mainloop* m = NULL;
15353a5a1b3Sopenharmony_ci    int k;
15453a5a1b3Sopenharmony_ci    struct timeval start, last_info = { 0, 0 };
15553a5a1b3Sopenharmony_ci    pa_usec_t old_t = 0, old_rtc = 0;
15653a5a1b3Sopenharmony_ci#ifdef CORK
15753a5a1b3Sopenharmony_ci    bool corked = false;
15853a5a1b3Sopenharmony_ci#endif
15953a5a1b3Sopenharmony_ci
16053a5a1b3Sopenharmony_ci    /* Set up a new main loop */
16153a5a1b3Sopenharmony_ci    m = pa_threaded_mainloop_new();
16253a5a1b3Sopenharmony_ci    fail_unless(m != NULL);
16353a5a1b3Sopenharmony_ci    mainloop_api = pa_threaded_mainloop_get_api(m);
16453a5a1b3Sopenharmony_ci    fail_unless(mainloop_api != NULL);
16553a5a1b3Sopenharmony_ci    context = pa_context_new(mainloop_api, bname);
16653a5a1b3Sopenharmony_ci    fail_unless(context != NULL);
16753a5a1b3Sopenharmony_ci
16853a5a1b3Sopenharmony_ci    pa_context_set_state_callback(context, context_state_callback, NULL);
16953a5a1b3Sopenharmony_ci
17053a5a1b3Sopenharmony_ci    fail_unless(pa_context_connect(context, NULL, 0, NULL) >= 0);
17153a5a1b3Sopenharmony_ci
17253a5a1b3Sopenharmony_ci    pa_gettimeofday(&start);
17353a5a1b3Sopenharmony_ci
17453a5a1b3Sopenharmony_ci    fail_unless(pa_threaded_mainloop_start(m) >= 0);
17553a5a1b3Sopenharmony_ci
17653a5a1b3Sopenharmony_ci/* #ifdef CORK */
17753a5a1b3Sopenharmony_ci    for (k = 0; k < 20000; k++)
17853a5a1b3Sopenharmony_ci/* #else */
17953a5a1b3Sopenharmony_ci/*     for (k = 0; k < 2000; k++) */
18053a5a1b3Sopenharmony_ci/* #endif */
18153a5a1b3Sopenharmony_ci    {
18253a5a1b3Sopenharmony_ci        bool success = false, changed = false;
18353a5a1b3Sopenharmony_ci        pa_usec_t t, rtc, d;
18453a5a1b3Sopenharmony_ci        struct timeval now, tv;
18553a5a1b3Sopenharmony_ci        bool playing = false;
18653a5a1b3Sopenharmony_ci
18753a5a1b3Sopenharmony_ci        pa_threaded_mainloop_lock(m);
18853a5a1b3Sopenharmony_ci
18953a5a1b3Sopenharmony_ci        if (stream) {
19053a5a1b3Sopenharmony_ci            const pa_timing_info *info;
19153a5a1b3Sopenharmony_ci
19253a5a1b3Sopenharmony_ci            if (pa_stream_get_time(stream, &t) >= 0 &&
19353a5a1b3Sopenharmony_ci                pa_stream_get_latency(stream, &d, NULL) >= 0)
19453a5a1b3Sopenharmony_ci                success = true;
19553a5a1b3Sopenharmony_ci
19653a5a1b3Sopenharmony_ci            if ((info = pa_stream_get_timing_info(stream))) {
19753a5a1b3Sopenharmony_ci                if (memcmp(&last_info, &info->timestamp, sizeof(struct timeval))) {
19853a5a1b3Sopenharmony_ci                    changed = true;
19953a5a1b3Sopenharmony_ci                    last_info = info->timestamp;
20053a5a1b3Sopenharmony_ci                }
20153a5a1b3Sopenharmony_ci                if (info->playing)
20253a5a1b3Sopenharmony_ci                    playing = true;
20353a5a1b3Sopenharmony_ci            }
20453a5a1b3Sopenharmony_ci        }
20553a5a1b3Sopenharmony_ci
20653a5a1b3Sopenharmony_ci        pa_threaded_mainloop_unlock(m);
20753a5a1b3Sopenharmony_ci
20853a5a1b3Sopenharmony_ci        pa_gettimeofday(&now);
20953a5a1b3Sopenharmony_ci
21053a5a1b3Sopenharmony_ci        if (success) {
21153a5a1b3Sopenharmony_ci#ifdef CORK
21253a5a1b3Sopenharmony_ci            bool cork_now;
21353a5a1b3Sopenharmony_ci#endif
21453a5a1b3Sopenharmony_ci            rtc = pa_timeval_diff(&now, &start);
21553a5a1b3Sopenharmony_ci            pa_log_info("%i\t%llu\t%llu\t%llu\t%llu\t%lli\t%u\t%u\t%llu\t%llu", k,
21653a5a1b3Sopenharmony_ci                   (unsigned long long) rtc,
21753a5a1b3Sopenharmony_ci                   (unsigned long long) t,
21853a5a1b3Sopenharmony_ci                   (unsigned long long) (rtc-old_rtc),
21953a5a1b3Sopenharmony_ci                   (unsigned long long) (t-old_t),
22053a5a1b3Sopenharmony_ci                   (signed long long) rtc - (signed long long) t,
22153a5a1b3Sopenharmony_ci                   changed,
22253a5a1b3Sopenharmony_ci                   playing,
22353a5a1b3Sopenharmony_ci                   (unsigned long long) latency,
22453a5a1b3Sopenharmony_ci                   (unsigned long long) d);
22553a5a1b3Sopenharmony_ci
22653a5a1b3Sopenharmony_ci            fflush(stdout);
22753a5a1b3Sopenharmony_ci            old_t = t;
22853a5a1b3Sopenharmony_ci            old_rtc = rtc;
22953a5a1b3Sopenharmony_ci
23053a5a1b3Sopenharmony_ci#ifdef CORK
23153a5a1b3Sopenharmony_ci            cork_now = (rtc / (2*PA_USEC_PER_SEC)) % 2 == 1;
23253a5a1b3Sopenharmony_ci
23353a5a1b3Sopenharmony_ci            if (corked != cork_now) {
23453a5a1b3Sopenharmony_ci                pa_threaded_mainloop_lock(m);
23553a5a1b3Sopenharmony_ci                pa_operation_unref(pa_stream_cork(stream, cork_now, NULL, NULL));
23653a5a1b3Sopenharmony_ci                pa_threaded_mainloop_unlock(m);
23753a5a1b3Sopenharmony_ci
23853a5a1b3Sopenharmony_ci                pa_log(cork_now ? "Corking" : "Uncorking");
23953a5a1b3Sopenharmony_ci
24053a5a1b3Sopenharmony_ci                corked = cork_now;
24153a5a1b3Sopenharmony_ci            }
24253a5a1b3Sopenharmony_ci#endif
24353a5a1b3Sopenharmony_ci        }
24453a5a1b3Sopenharmony_ci
24553a5a1b3Sopenharmony_ci        /* Spin loop, ugly but normal usleep() is just too badly grained */
24653a5a1b3Sopenharmony_ci        tv = now;
24753a5a1b3Sopenharmony_ci        while (pa_timeval_diff(pa_gettimeofday(&now), &tv) < 1000)
24853a5a1b3Sopenharmony_ci            pa_thread_yield();
24953a5a1b3Sopenharmony_ci    }
25053a5a1b3Sopenharmony_ci
25153a5a1b3Sopenharmony_ci    if (m)
25253a5a1b3Sopenharmony_ci        pa_threaded_mainloop_stop(m);
25353a5a1b3Sopenharmony_ci
25453a5a1b3Sopenharmony_ci    if (stream) {
25553a5a1b3Sopenharmony_ci        pa_stream_disconnect(stream);
25653a5a1b3Sopenharmony_ci        pa_stream_unref(stream);
25753a5a1b3Sopenharmony_ci    }
25853a5a1b3Sopenharmony_ci
25953a5a1b3Sopenharmony_ci    if (context) {
26053a5a1b3Sopenharmony_ci        pa_context_disconnect(context);
26153a5a1b3Sopenharmony_ci        pa_context_unref(context);
26253a5a1b3Sopenharmony_ci    }
26353a5a1b3Sopenharmony_ci
26453a5a1b3Sopenharmony_ci    if (m)
26553a5a1b3Sopenharmony_ci        pa_threaded_mainloop_free(m);
26653a5a1b3Sopenharmony_ci}
26753a5a1b3Sopenharmony_ciEND_TEST
26853a5a1b3Sopenharmony_ci
26953a5a1b3Sopenharmony_ciint main(int argc, char *argv[]) {
27053a5a1b3Sopenharmony_ci    int failed = 0;
27153a5a1b3Sopenharmony_ci    Suite *s;
27253a5a1b3Sopenharmony_ci    TCase *tc;
27353a5a1b3Sopenharmony_ci    SRunner *sr;
27453a5a1b3Sopenharmony_ci
27553a5a1b3Sopenharmony_ci    if (!getenv("MAKE_CHECK"))
27653a5a1b3Sopenharmony_ci        pa_log_set_level(PA_LOG_DEBUG);
27753a5a1b3Sopenharmony_ci
27853a5a1b3Sopenharmony_ci    bname = argv[0];
27953a5a1b3Sopenharmony_ci    playback = argc <= 1 || !pa_streq(argv[1], "-r");
28053a5a1b3Sopenharmony_ci    latency = (argc >= 2 && !pa_streq(argv[1], "-r")) ? atoi(argv[1]) : (argc >= 3 ? atoi(argv[2]) : 0);
28153a5a1b3Sopenharmony_ci
28253a5a1b3Sopenharmony_ci    s = suite_create("Interpol");
28353a5a1b3Sopenharmony_ci    tc = tcase_create("interpol");
28453a5a1b3Sopenharmony_ci    tcase_add_test(tc, interpol_test);
28553a5a1b3Sopenharmony_ci    tcase_set_timeout(tc, 5 * 60);
28653a5a1b3Sopenharmony_ci    suite_add_tcase(s, tc);
28753a5a1b3Sopenharmony_ci
28853a5a1b3Sopenharmony_ci    sr = srunner_create(s);
28953a5a1b3Sopenharmony_ci    srunner_run_all(sr, CK_NORMAL);
29053a5a1b3Sopenharmony_ci    failed = srunner_ntests_failed(sr);
29153a5a1b3Sopenharmony_ci    srunner_free(sr);
29253a5a1b3Sopenharmony_ci
29353a5a1b3Sopenharmony_ci    return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
29453a5a1b3Sopenharmony_ci}
295