Beispiel #1
0
static void stream_read_cb(pa_stream *s, size_t length, void *userdata)
{
    demux_t *demux = userdata;
    demux_sys_t *sys = demux->p_sys;
    const void *ptr;
    unsigned samples = length / sys->framesize;

    if (pa_stream_peek(s, &ptr, &length) < 0) {
        vlc_pa_error(demux, "cannot peek stream", sys->context);
        return;
    }

    mtime_t pts = mdate();
    pa_usec_t latency;
    int negative;

    if (pa_stream_get_latency(s, &latency, &negative) < 0) {
        vlc_pa_error(demux, "cannot determine latency", sys->context);
        return;
    }
    if (negative)
        pts += latency;
    else
        pts -= latency;

    es_out_Control(demux->out, ES_OUT_SET_PCR, pts);
    if (unlikely(sys->es == NULL))
        goto race;

    block_t *block = block_Alloc(length);
    if (likely(block != NULL)) {
        memcpy(block->p_buffer, ptr, length);
        block->i_nb_samples = samples;
        block->i_dts = block->i_pts = pts;
        if (sys->discontinuity) {
            block->i_flags |= BLOCK_FLAG_DISCONTINUITY;
            sys->discontinuity = false;
        }

        es_out_Send(demux->out, sys->es, block);
    } else
        sys->discontinuity = true;
race:
    pa_stream_drop(s);
}
Beispiel #2
0
/**
 * Gets latency of a PulseAudio stream.
 * \return the latency or VLC_TS_INVALID on error.
 */
mtime_t vlc_pa_get_latency(vlc_object_t *obj, pa_context *ctx, pa_stream *s)
{
    pa_usec_t latency;
    int negative;

    if (pa_stream_get_latency(s, &latency, &negative)) {
        if (pa_context_errno (ctx) != PA_ERR_NODATA)
            vlc_pa_error(obj, "unknown latency", ctx);
        return VLC_TS_INVALID;
    }
    return negative ? -latency : +latency;
}
Beispiel #3
0
/**
 * Initializes the PulseAudio main loop and connects to the PulseAudio server.
 * @return a PulseAudio context on success, or NULL on error
 */
pa_context *vlc_pa_connect (vlc_object_t *obj, pa_threaded_mainloop **mlp)
{
    msg_Dbg (obj, "using library version %s", pa_get_library_version ());
    msg_Dbg (obj, " (compiled with version %s, protocol %u)",
             pa_get_headers_version (), PA_PROTOCOL_VERSION);

    /* Initialize main loop */
    pa_threaded_mainloop *mainloop = pa_threaded_mainloop_new ();
    if (unlikely(mainloop == NULL))
        return NULL;

    if (pa_threaded_mainloop_start (mainloop) < 0)
    {
        pa_threaded_mainloop_free (mainloop);
        return NULL;
    }

    /* Fill in context (client) properties */
    char *ua = var_InheritString (obj, "user-agent");
    pa_proplist *props = pa_proplist_new ();
    if (likely(props != NULL))
    {
        pa_proplist_sets (props, PA_PROP_APPLICATION_NAME, ua);
        pa_proplist_sets (props, PA_PROP_APPLICATION_ID, "org.VideoLAN.VLC");
        pa_proplist_sets (props, PA_PROP_APPLICATION_VERSION, PACKAGE_VERSION);
        pa_proplist_sets (props, PA_PROP_APPLICATION_ICON_NAME, PACKAGE_NAME);
        //pa_proplist_sets (props, PA_PROP_APPLICATION_LANGUAGE, _("C"));
        pa_proplist_sets (props, PA_PROP_APPLICATION_LANGUAGE,
                          setlocale (LC_MESSAGES, NULL));

        pa_proplist_setf (props, PA_PROP_APPLICATION_PROCESS_ID, "%lu",
                          (unsigned long) getpid ());
        //pa_proplist_sets (props, PA_PROP_APPLICATION_PROCESS_BINARY,
        //                  PACKAGE_NAME);

        for (size_t max = sysconf (_SC_GETPW_R_SIZE_MAX), len = max % 1024 + 1024;
             len < max; len += 1024)
        {
            struct passwd pwbuf, *pw;
            char buf[len];

            if (getpwuid_r (getuid (), &pwbuf, buf, sizeof (buf), &pw) == 0
             && pw != NULL)
                pa_proplist_sets (props, PA_PROP_APPLICATION_PROCESS_USER,
                                  pw->pw_name);
        }

        for (size_t max = sysconf (_SC_HOST_NAME_MAX), len = max % 1024 + 1024;
             len < max; len += 1024)
        {
            char hostname[len];

            if (gethostname (hostname, sizeof (hostname)) == 0)
                pa_proplist_sets (props, PA_PROP_APPLICATION_PROCESS_HOST,
                                  hostname);
        }

        const char *session = getenv ("XDG_SESSION_COOKIE");
        if (session != NULL)
        {
            pa_proplist_setf (props, PA_PROP_APPLICATION_PROCESS_MACHINE_ID,
                              "%.32s", session); /* XXX: is this valid? */
            pa_proplist_sets (props, PA_PROP_APPLICATION_PROCESS_SESSION_ID,
                              session);
        }
    }

    /* Connect to PulseAudio daemon */
    pa_context *ctx;
    pa_mainloop_api *api;

    pa_threaded_mainloop_lock (mainloop);
    api = pa_threaded_mainloop_get_api (mainloop);
    ctx = pa_context_new_with_proplist (api, ua, props);
    free (ua);
    if (props != NULL)
        pa_proplist_free (props);
    if (unlikely(ctx == NULL))
        goto fail;

    pa_context_set_state_callback (ctx, context_state_cb, mainloop);
    if (pa_context_connect (ctx, NULL, 0, NULL) < 0
     || context_wait (ctx, mainloop))
    {
        vlc_pa_error (obj, "PulseAudio server connection failure", ctx);
        pa_context_unref (ctx);
        goto fail;
    }
    msg_Dbg (obj, "connected %s to %s as client #%"PRIu32,
             pa_context_is_local (ctx) ? "locally" : "remotely",
             pa_context_get_server (ctx), pa_context_get_index (ctx));
    msg_Dbg (obj, "using protocol %"PRIu32", server protocol %"PRIu32,
             pa_context_get_protocol_version (ctx),
             pa_context_get_server_protocol_version (ctx));

    pa_threaded_mainloop_unlock (mainloop);
    *mlp = mainloop;
    return ctx;

fail:
    pa_threaded_mainloop_unlock (mainloop);
    pa_threaded_mainloop_stop (mainloop);
    pa_threaded_mainloop_free (mainloop);
    return NULL;
}
Beispiel #4
0
static int Open(vlc_object_t *obj)
{
    demux_t *demux = (demux_t *)obj;

    demux_sys_t *sys = malloc(sizeof (*sys));
    if (unlikely(sys == NULL))
        return VLC_ENOMEM;

    sys->context = vlc_pa_connect(obj, &sys->mainloop);
    if (sys->context == NULL) {
        free(sys);
        return VLC_EGENERIC;
    }

    sys->stream = NULL;
    sys->es = NULL;
    sys->discontinuity = false;
    sys->caching = INT64_C(1000) * var_InheritInteger(obj, "live-caching");
    demux->p_sys = sys;

    /* Stream parameters */
    struct pa_sample_spec ss;
    ss.format = PA_SAMPLE_S16NE;
    ss.rate = 48000;
    ss.channels = 2;
    assert(pa_sample_spec_valid(&ss));

    struct pa_channel_map map;
    map.channels = 2;
    map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
    map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
    assert(pa_channel_map_valid(&map));

    const pa_stream_flags_t flags = PA_STREAM_INTERPOLATE_TIMING
                                  | PA_STREAM_AUTO_TIMING_UPDATE
                                  | PA_STREAM_ADJUST_LATENCY
                                  | PA_STREAM_FIX_FORMAT
                                  | PA_STREAM_FIX_RATE
                                  /*| PA_STREAM_FIX_CHANNELS*/;

    const char *dev = NULL;
    if (demux->psz_location != NULL && demux->psz_location[0] != '\0')
        dev = demux->psz_location;

    struct pa_buffer_attr attr = {
        .maxlength = -1,
        .fragsize = pa_usec_to_bytes(sys->caching, &ss) / 2,
    };

    es_format_t fmt;

    /* Create record stream */
    pa_stream *s;
    pa_operation *op;

    pa_threaded_mainloop_lock(sys->mainloop);
    s = pa_stream_new(sys->context, "audio stream", &ss, &map);
    if (s == NULL)
        goto error;

    sys->stream = s;
    pa_stream_set_state_callback(s, stream_state_cb, sys->mainloop);
    pa_stream_set_read_callback(s, stream_read_cb, demux);
    pa_stream_set_buffer_attr_callback(s, stream_buffer_attr_cb, demux);
    pa_stream_set_moved_callback(s, stream_moved_cb, demux);
    pa_stream_set_overflow_callback(s, stream_overflow_cb, demux);
    pa_stream_set_started_callback(s, stream_started_cb, demux);
    pa_stream_set_suspended_callback(s, stream_suspended_cb, demux);
    pa_stream_set_underflow_callback(s, stream_underflow_cb, demux);

    if (pa_stream_connect_record(s, dev, &attr, flags) < 0
     || stream_wait(s, sys->mainloop)) {
        vlc_pa_error(obj, "cannot connect record stream", sys->context);
        goto error;
    }

    /* The ES should be initialized before stream_read_cb(), but how? */
    const struct pa_sample_spec *pss = pa_stream_get_sample_spec(s);
    if ((unsigned)pss->format >= sizeof (fourccs) / sizeof (fourccs[0])) {
        msg_Err(obj, "unknown PulseAudio sample format %u",
                (unsigned)pss->format);
        goto error;
    }

    vlc_fourcc_t format = fourccs[pss->format];
    if (format == 0) { /* FIXME: should renegotiate something else */
        msg_Err(obj, "unsupported PulseAudio sample format %u",
                (unsigned)pss->format);
        goto error;
    }

    es_format_Init(&fmt, AUDIO_ES, format);
    fmt.audio.i_physical_channels = fmt.audio.i_original_channels =
        AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
    fmt.audio.i_channels = ss.channels;
    fmt.audio.i_rate = pss->rate;
    fmt.audio.i_bitspersample = aout_BitsPerSample(format);
    fmt.audio.i_blockalign = fmt.audio.i_bitspersample * ss.channels / 8;
    fmt.i_bitrate = fmt.audio.i_bitspersample * ss.channels * pss->rate;
    sys->framesize = fmt.audio.i_blockalign;
    sys->es = es_out_Add (demux->out, &fmt);

    /* Update the buffer attributes according to actual format */
    attr.fragsize = pa_usec_to_bytes(sys->caching, pss) / 2;
    op = pa_stream_set_buffer_attr(s, &attr, stream_success_cb, sys->mainloop);
    if (likely(op != NULL)) {
        while (pa_operation_get_state(op) == PA_OPERATION_RUNNING)
            pa_threaded_mainloop_wait(sys->mainloop);
        pa_operation_unref(op);
    }
    stream_buffer_attr_cb(s, demux);
    pa_threaded_mainloop_unlock(sys->mainloop);

    demux->pf_demux = NULL;
    demux->pf_control = Control;
    return VLC_SUCCESS;

error:
    pa_threaded_mainloop_unlock(sys->mainloop);
    Close(obj);
    return VLC_EGENERIC;
}

static void Close (vlc_object_t *obj)
{
    demux_t *demux = (demux_t *)obj;
    demux_sys_t *sys = demux->p_sys;
    pa_stream *s = sys->stream;

    if (likely(s != NULL)) {
        pa_threaded_mainloop_lock(sys->mainloop);
        pa_stream_disconnect(s);
        pa_stream_set_state_callback(s, NULL, NULL);
        pa_stream_set_read_callback(s, NULL, NULL);
        pa_stream_set_buffer_attr_callback(s, NULL, NULL);
        pa_stream_set_moved_callback(s, NULL, NULL);
        pa_stream_set_overflow_callback(s, NULL, NULL);
        pa_stream_set_started_callback(s, NULL, NULL);
        pa_stream_set_suspended_callback(s, NULL, NULL);
        pa_stream_set_underflow_callback(s, NULL, NULL);
        pa_stream_unref(s);
        pa_threaded_mainloop_unlock(sys->mainloop);
    }

    vlc_pa_disconnect(obj, sys->context, sys->mainloop);
    free(sys);
}