static int init(struct ao *ao) { struct priv *priv = ao->priv; if (rsd_init(&priv->rd) < 0) return -1; // Actual channel layout unknown. struct mp_chmap_sel sel = {0}; mp_chmap_sel_add_waveext_def(&sel); if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels)) { rsd_free(priv->rd); return -1; } rsd_set_param(priv->rd, RSD_SAMPLERATE, (int[]) { ao->samplerate });
static int init(struct ao *ao) { struct priv *priv = ao->priv; if (rsd_init(&priv->rd) < 0) return -1; if (priv->host && priv->host[0]) rsd_set_param(priv->rd, RSD_HOST, priv->host); if (priv->port && priv->port[0]) rsd_set_param(priv->rd, RSD_PORT, priv->port); // Actual channel layout unknown. struct mp_chmap_sel sel = {0}; mp_chmap_sel_add_waveext_def(&sel); if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels)) { rsd_free(priv->rd); return -1; } rsd_set_param(priv->rd, RSD_SAMPLERATE, &ao->samplerate); rsd_set_param(priv->rd, RSD_CHANNELS, &ao->channels.num); ao->format = af_fmt_from_planar(ao->format); int rsd_format = set_format(ao); rsd_set_param(priv->rd, RSD_FORMAT, &rsd_format); if (rsd_start(priv->rd) < 0) { rsd_free(priv->rd); return -1; } return 0; }
static int init(struct ao *ao) { if (SDL_WasInit(SDL_INIT_AUDIO)) { mp_msg(MSGT_AO, MSGL_ERR, "[sdl] already initialized\n"); return -1; } struct priv *priv = ao->priv; if (SDL_InitSubSystem(SDL_INIT_AUDIO)) { if (!ao->probing) mp_msg(MSGT_AO, MSGL_ERR, "[sdl] SDL_Init failed\n"); uninit(ao, true); return -1; } struct mp_chmap_sel sel = {0}; mp_chmap_sel_add_waveext_def(&sel); if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels)) { uninit(ao, true); return -1; } SDL_AudioSpec desired, obtained; switch (ao->format) { case AF_FORMAT_U8: desired.format = AUDIO_U8; break; case AF_FORMAT_S8: desired.format = AUDIO_S8; break; case AF_FORMAT_U16_LE: desired.format = AUDIO_U16LSB; break; case AF_FORMAT_U16_BE: desired.format = AUDIO_U16MSB; break; default: case AF_FORMAT_S16_LE: desired.format = AUDIO_S16LSB; break; case AF_FORMAT_S16_BE: desired.format = AUDIO_S16MSB; break; #ifdef AUDIO_S32LSB case AF_FORMAT_S32_LE: desired.format = AUDIO_S32LSB; break; #endif #ifdef AUDIO_S32MSB case AF_FORMAT_S32_BE: desired.format = AUDIO_S32MSB; break; #endif #ifdef AUDIO_F32LSB case AF_FORMAT_FLOAT_LE: desired.format = AUDIO_F32LSB; break; #endif #ifdef AUDIO_F32MSB case AF_FORMAT_FLOAT_BE: desired.format = AUDIO_F32MSB; break; #endif } desired.freq = ao->samplerate; desired.channels = ao->channels.num; desired.samples = FFMIN(32768, ceil_power_of_two(ao->samplerate * priv->buflen)); desired.callback = audio_callback; desired.userdata = ao; mp_msg(MSGT_AO, MSGL_V, "[sdl] requested format: %d Hz, %d channels, %x, " "buffer size: %d samples\n", (int) desired.freq, (int) desired.channels, (int) desired.format, (int) desired.samples); obtained = desired; if (SDL_OpenAudio(&desired, &obtained)) { if (!ao->probing) mp_msg(MSGT_AO, MSGL_ERR, "[sdl] could not open audio: %s\n", SDL_GetError()); uninit(ao, true); return -1; } mp_msg(MSGT_AO, MSGL_V, "[sdl] obtained format: %d Hz, %d channels, %x, " "buffer size: %d samples\n", (int) obtained.freq, (int) obtained.channels, (int) obtained.format, (int) obtained.samples); switch (obtained.format) { case AUDIO_U8: ao->format = AF_FORMAT_U8; break; case AUDIO_S8: ao->format = AF_FORMAT_S8; break; case AUDIO_S16LSB: ao->format = AF_FORMAT_S16_LE; break; case AUDIO_S16MSB: ao->format = AF_FORMAT_S16_BE; break; case AUDIO_U16LSB: ao->format = AF_FORMAT_U16_LE; break; case AUDIO_U16MSB: ao->format = AF_FORMAT_U16_BE; break; #ifdef AUDIO_S32LSB case AUDIO_S32LSB: ao->format = AF_FORMAT_S32_LE; break; #endif #ifdef AUDIO_S32MSB case AUDIO_S32MSB: ao->format = AF_FORMAT_S32_BE; break; #endif #ifdef AUDIO_F32LSB case AUDIO_F32LSB: ao->format = AF_FORMAT_FLOAT_LE; break; #endif #ifdef AUDIO_F32MSB case AUDIO_F32MSB: ao->format = AF_FORMAT_FLOAT_BE; break; #endif default: if (!ao->probing) mp_msg(MSGT_AO, MSGL_ERR, "[sdl] could not find matching format\n"); uninit(ao, true); return -1; } if (!ao_chmap_sel_get_def(ao, &sel, &ao->channels, obtained.channels)) { uninit(ao, true); return -1; } ao->samplerate = obtained.freq; priv->buffer = av_fifo_alloc(obtained.size * priv->bufcnt); priv->buffer_mutex = SDL_CreateMutex(); if (!priv->buffer_mutex) { mp_msg(MSGT_AO, MSGL_ERR, "[sdl] SDL_CreateMutex failed\n"); uninit(ao, true); return -1; } priv->underrun_cond = SDL_CreateCond(); if (!priv->underrun_cond) { mp_msg(MSGT_AO, MSGL_ERR, "[sdl] SDL_CreateCond failed\n"); uninit(ao, true); return -1; } priv->unpause = 1; priv->paused = 1; priv->callback_time0 = priv->callback_time1 = mp_time_us(); return 1; }
static int init(struct ao *ao) { if (SDL_WasInit(SDL_INIT_AUDIO)) { MP_ERR(ao, "already initialized\n"); return -1; } struct priv *priv = ao->priv; if (SDL_InitSubSystem(SDL_INIT_AUDIO)) { if (!ao->probing) MP_ERR(ao, "SDL_Init failed\n"); uninit(ao); return -1; } struct mp_chmap_sel sel = {0}; mp_chmap_sel_add_waveext_def(&sel); if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels)) { uninit(ao); return -1; } ao->format = af_fmt_from_planar(ao->format); SDL_AudioSpec desired, obtained; desired.format = AUDIO_S16SYS; for (int n = 0; fmtmap[n][0]; n++) { if (ao->format == fmtmap[n][0]) { desired.format = fmtmap[n][1]; break; } } desired.freq = ao->samplerate; desired.channels = ao->channels.num; desired.samples = MPMIN(32768, ceil_power_of_two(ao->samplerate * priv->buflen)); desired.callback = audio_callback; desired.userdata = ao; MP_VERBOSE(ao, "requested format: %d Hz, %d channels, %x, " "buffer size: %d samples\n", (int) desired.freq, (int) desired.channels, (int) desired.format, (int) desired.samples); obtained = desired; if (SDL_OpenAudio(&desired, &obtained)) { if (!ao->probing) MP_ERR(ao, "could not open audio: %s\n", SDL_GetError()); uninit(ao); return -1; } MP_VERBOSE(ao, "obtained format: %d Hz, %d channels, %x, " "buffer size: %d samples\n", (int) obtained.freq, (int) obtained.channels, (int) obtained.format, (int) obtained.samples); // The sample count is usually the number of samples the callback requests, // which we assume is the period size. Normally, ao.c will allocate a large // enough buffer. But in case the period size should be pathologically // large, this will help. ao->device_buffer = 3 * obtained.samples; ao->format = 0; for (int n = 0; fmtmap[n][0]; n++) { if (obtained.format == fmtmap[n][1]) { ao->format = fmtmap[n][0]; break; } } if (!ao->format) { if (!ao->probing) MP_ERR(ao, "could not find matching format\n"); uninit(ao); return -1; } if (!ao_chmap_sel_get_def(ao, &sel, &ao->channels, obtained.channels)) { uninit(ao); return -1; } ao->samplerate = obtained.freq; priv->paused = 1; return 1; }
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} }, };