コード例 #1
0
ファイル: ao_jack.c プロジェクト: muflax-scholars/mpv
static int init(struct ao *ao)
{
    struct priv *p = ao->priv;
    const char **matching_ports = NULL;
    char *port_name = p->cfg_port && p->cfg_port[0] ? p->cfg_port : NULL;
    jack_options_t open_options = JackNullOption;
    int port_flags = JackPortIsInput;
    int i;

    struct mp_chmap_sel sel = {0};

    if (p->stdlayout == 0) {
        mp_chmap_sel_add_waveext(&sel);
    } else if (p->stdlayout == 1) {
        mp_chmap_sel_add_alsa_def(&sel);
    } else {
        mp_chmap_sel_add_any(&sel);
    }

    if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels))
        goto err_out;

    if (!p->autostart)
        open_options |= JackNoStartServer;
    p->client = jack_client_open(p->cfg_client_name, open_options, NULL);
    if (!p->client) {
        MP_FATAL(ao, "cannot open server\n");
        goto err_out;
    }
    jack_set_process_callback(p->client, outputaudio, ao);

    // list matching ports if connections should be made
    if (p->connect) {
        if (!port_name)
            port_flags |= JackPortIsPhysical;
        matching_ports = jack_get_ports(p->client, port_name, NULL, port_flags);
        if (!matching_ports || !matching_ports[0]) {
            MP_FATAL(ao, "no physical ports available\n");
            goto err_out;
        }
        i = 1;
        p->num_ports = ao->channels.num;
        while (matching_ports[i])
            i++;
        if (p->num_ports > i)
            p->num_ports = i;
    }

    // create out output ports
    for (i = 0; i < p->num_ports; i++) {
        char pname[30];
        snprintf(pname, 30, "out_%d", i);
        p->ports[i] =
            jack_port_register(p->client, pname, JACK_DEFAULT_AUDIO_TYPE,
                               JackPortIsOutput, 0);
        if (!p->ports[i]) {
            MP_FATAL(ao, "not enough ports available\n");
            goto err_out;
        }
    }
    if (jack_activate(p->client)) {
        MP_FATAL(ao, "activate failed\n");
        goto err_out;
    }
    for (i = 0; i < p->num_ports; i++) {
        if (jack_connect(p->client, jack_port_name(p->ports[i]),
                         matching_ports[i]))
        {
            MP_FATAL(ao, "connecting failed\n");
            goto err_out;
        }
    }
    ao->samplerate = jack_get_sample_rate(p->client);
    jack_latency_range_t jack_latency_range;
    jack_port_get_latency_range(p->ports[0], JackPlaybackLatency,
                                &jack_latency_range);
    p->jack_latency = (float)(jack_latency_range.max + jack_get_buffer_size(p->client))
                      / (float)ao->samplerate;
    p->callback_interval = 0;

    if (!ao_chmap_sel_get_def(ao, &sel, &ao->channels, p->num_ports))
        goto err_out;

    ao->format = AF_FORMAT_FLOAT_NE;
    int unitsize = ao->channels.num * sizeof(float);
    p->outburst = (CHUNK_SIZE + unitsize - 1) / unitsize * unitsize;
    p->ring = mp_ring_new(p, NUM_CHUNKS * p->outburst);
    free(matching_ports);
    return 0;

err_out:
    free(matching_ports);
    if (p->client)
        jack_client_close(p->client);
    return -1;
}
コード例 #2
0
ファイル: ao_portaudio.c プロジェクト: CrimsonVoid/mpv
static int init(struct ao *ao)
{
    struct priv *priv = ao->priv;

    if (!check_pa_ret(Pa_Initialize()))
        return -1;

    pthread_mutex_init(&priv->ring_mutex, NULL);

    int pa_device = Pa_GetDefaultOutputDevice();
    if (priv->cfg_device && priv->cfg_device[0])
        pa_device = find_device(priv->cfg_device);
    if (pa_device == paNoDevice)
        goto error_exit;

    // The actual channel order probably depends on the platform.
    struct mp_chmap_sel sel = {0};
    mp_chmap_sel_add_waveext_def(&sel);
    if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels))
        goto error_exit;

    PaStreamParameters sp = {
        .device = pa_device,
        .channelCount = ao->channels.num,
        .suggestedLatency
            = Pa_GetDeviceInfo(pa_device)->defaultHighOutputLatency,
    };

    ao->format = af_fmt_from_planar(ao->format);

    const struct format_map *fmt = format_maps;
    while (fmt->pa_format) {
        if (fmt->mp_format == ao->format) {
            PaStreamParameters test = sp;
            test.sampleFormat = fmt->pa_format;
            if (Pa_IsFormatSupported(NULL, &test, ao->samplerate) == paNoError)
                break;
        }
        fmt++;
    }
    if (!fmt->pa_format) {
        MP_VERBOSE(ao, "Unsupported format, using default.\n");
        fmt = format_maps;
    }

    ao->format = fmt->mp_format;
    sp.sampleFormat = fmt->pa_format;
    priv->framelen = ao->channels.num * (af_fmt2bits(ao->format) / 8);
    ao->bps = ao->samplerate * priv->framelen;

    if (!check_pa_ret(Pa_IsFormatSupported(NULL, &sp, ao->samplerate)))
        goto error_exit;
    if (!check_pa_ret(Pa_OpenStream(&priv->stream, NULL, &sp, ao->samplerate,
                                    paFramesPerBufferUnspecified, paNoFlag,
                                    stream_callback, ao)))
        goto error_exit;

    priv->ring = mp_ring_new(priv, seconds_to_bytes(ao, 0.5));

    return 0;

error_exit:
    uninit(ao, true);
    return -1;
}

static int play(struct ao *ao, void **data, int samples, int flags)
{
    struct priv *priv = ao->priv;

    pthread_mutex_lock(&priv->ring_mutex);

    int write_len = mp_ring_write(priv->ring, data[0], samples * ao->sstride);
    if (flags & AOPLAY_FINAL_CHUNK)
        priv->play_remaining = true;

    pthread_mutex_unlock(&priv->ring_mutex);

    if (Pa_IsStreamStopped(priv->stream) == 1)
        check_pa_ret(Pa_StartStream(priv->stream));

    return write_len / ao->sstride;
}

static int get_space(struct ao *ao)
{
    struct priv *priv = ao->priv;

    pthread_mutex_lock(&priv->ring_mutex);

    int free = mp_ring_available(priv->ring);

    pthread_mutex_unlock(&priv->ring_mutex);

    return free / ao->sstride;
}

static float get_delay(struct ao *ao)
{
    struct priv *priv = ao->priv;

    double stream_time = Pa_GetStreamTime(priv->stream);

    pthread_mutex_lock(&priv->ring_mutex);

    float frame_time = priv->play_time ? priv->play_time - stream_time : 0;
    float buffer_latency = (mp_ring_buffered(priv->ring) + priv->play_silence)
                           / (float)ao->bps;

    pthread_mutex_unlock(&priv->ring_mutex);

    return buffer_latency + frame_time;
}

static void reset(struct ao *ao)
{
    struct priv *priv = ao->priv;

    if (Pa_IsStreamStopped(priv->stream) != 1)
        check_pa_ret(Pa_AbortStream(priv->stream));

    pthread_mutex_lock(&priv->ring_mutex);

    mp_ring_reset(priv->ring);
    priv->play_remaining = false;
    priv->play_time = 0;
    priv->play_silence = 0;

    pthread_mutex_unlock(&priv->ring_mutex);
}

static void pause(struct ao *ao)
{
    struct priv *priv = ao->priv;

    check_pa_ret(Pa_AbortStream(priv->stream));

    double stream_time = Pa_GetStreamTime(priv->stream);

    pthread_mutex_lock(&priv->ring_mutex);

    // When playback resumes, replace the lost audio (due to dropping the
    // portaudio/driver/hardware internal buffers) with silence.
    float frame_time = priv->play_time ? priv->play_time - stream_time : 0;
    priv->play_silence += seconds_to_bytes(ao, FFMAX(frame_time, 0));
    priv->play_time = 0;

    pthread_mutex_unlock(&priv->ring_mutex);
}

static void resume(struct ao *ao)
{
    struct priv *priv = ao->priv;

    check_pa_ret(Pa_StartStream(priv->stream));
}

#define OPT_BASE_STRUCT struct priv

const struct ao_driver audio_out_portaudio = {
    .description = "PortAudio",
    .name      = "portaudio",
    .init      = init,
    .uninit    = uninit,
    .reset     = reset,
    .get_space = get_space,
    .play      = play,
    .get_delay = get_delay,
    .pause     = pause,
    .resume    = resume,
    .priv_size = sizeof(struct priv),
    .options = (const struct m_option[]) {
        OPT_STRING_VALIDATE("device", cfg_device, 0, validate_device_opt),
        {0}
    },
};