static void ext_stream_restore_subscribe_cb(pa_context *context, void *data) { Q_ASSERT(context); Q_ASSERT(data); if (!PAOperation(pa_ext_stream_restore_read(context, ext_stream_restore_read_cb, data))) { qCWarning(PLASMAPA) << "pa_ext_stream_restore_read() failed"; } }
void PulseAudioSinksManager::setDefaultSink(const QString& name) { defaultDevice = name; std::string std_name = defaultDevice.toStdString(); qDebug() << "setting sink to "<<name<<", and from std_String to "<<std_name.c_str(); PaOperation op(pa_context_set_default_sink (pa_ctx.get(),std_name.c_str(),NULL,NULL)); if (!op) { //g_warning ("pa_context_set_default_sink() failed: %s", // pa_strerror (pa_context_errno (control->priv->pa_context))); return; } op = pa_ext_stream_restore_read (pa_ctx.get(), PulseAudioSinksManager::pulseAudioMixerControlStreamRestoreCallback, this); if (!op) { return; } }
void Context::contextStateCallback(pa_context *c) { qCDebug(PLASMAPA) << "state callback"; pa_context_state_t state = pa_context_get_state(c); if (state == PA_CONTEXT_READY) { qCDebug(PLASMAPA) << "ready"; // 1. Register for the stream changes (except during probe) if (m_context == c) { pa_context_set_subscribe_callback(c, subscribe_cb, this); if (!PAOperation(pa_context_subscribe(c, (pa_subscription_mask_t) (PA_SUBSCRIPTION_MASK_SINK| PA_SUBSCRIPTION_MASK_SOURCE| PA_SUBSCRIPTION_MASK_CLIENT| PA_SUBSCRIPTION_MASK_SINK_INPUT| PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT| PA_SUBSCRIPTION_MASK_CARD| PA_SUBSCRIPTION_MASK_SERVER), nullptr, nullptr))) { qCWarning(PLASMAPA) << "pa_context_subscribe() failed"; return; } } if (!PAOperation(pa_context_get_sink_info_list(c, sink_cb, this))) { qCWarning(PLASMAPA) << "pa_context_get_sink_info_list() failed"; return; } if (!PAOperation(pa_context_get_source_info_list(c, source_cb, this))) { qCWarning(PLASMAPA) << "pa_context_get_source_info_list() failed"; return; } if (!PAOperation(pa_context_get_client_info_list(c, client_cb, this))) { qCWarning(PLASMAPA) << "pa_context_client_info_list() failed"; return; } if (!PAOperation(pa_context_get_card_info_list(c, card_cb, this))) { qCWarning(PLASMAPA) << "pa_context_get_card_info_list() failed"; return; } if (!PAOperation(pa_context_get_sink_input_info_list(c, sink_input_callback, this))) { qCWarning(PLASMAPA) << "pa_context_get_sink_input_info_list() failed"; return; } if (!PAOperation(pa_context_get_source_output_info_list(c, source_output_cb, this))) { qCWarning(PLASMAPA) << "pa_context_get_source_output_info_list() failed"; return; } if (!PAOperation(pa_context_get_server_info(c, server_cb, this))) { qCWarning(PLASMAPA) << "pa_context_get_server_info() failed"; return; } if (PAOperation(pa_ext_stream_restore_read(c, ext_stream_restore_read_cb, this))) { pa_ext_stream_restore_set_subscribe_cb(c, ext_stream_restore_subscribe_cb, this); PAOperation(pa_ext_stream_restore_subscribe(c, 1, nullptr, this)); } else { qCWarning(PLASMAPA) << "Failed to initialize stream_restore extension"; } } else if (!PA_CONTEXT_IS_GOOD(state)) { qCWarning(PLASMAPA) << "context kaput"; if (m_context) { pa_context_unref(m_context); m_context = nullptr; } reset(); QTimer::singleShot(1000, this, &Context::connectToDaemon); } }
void PulseAudioSystem::restore_sink_input_list_callback(pa_context *c, const pa_sink_input_info *i, int eol, void *userdata) { PulseAudioSystem *pas = reinterpret_cast<PulseAudioSystem *>(userdata); if (eol == 0) { // if we were tracking this specific sink previously if (pas->qhVolumes.contains(i->index)) { // and if it has the attenuated volume we applied to it if (pa_cvolume_equal(&i->volume, &pas->qhVolumes[i->index].attenuated_volume) != 0) { // mark it as matched pas->qlMatchedSinks.append(i->index); // reset the volume to normal pas->iRemainingOperations++; pa_operation_unref(pa_context_set_sink_input_volume(c, i->index, &pas->qhVolumes[i->index].normal_volume, restore_volume_success_callback, pas)); } // otherwise, save for matching at the end of iteration } else { QString restore_id = QLatin1String(pa_proplist_gets(i->proplist, "module-stream-restore.id")); PulseAttenuation patt; patt.index = i->index; patt.normal_volume = i->volume; pas->qhUnmatchedSinks[restore_id] = patt; } } else if (eol < 0) { qWarning("PulseAudio: Sink input introspection error."); } else { // build a list of missing streams by iterating our active list QHash<uint32_t, PulseAttenuation>::const_iterator it; for (it = pas->qhVolumes.constBegin(); it != pas->qhVolumes.constEnd(); ++it) { // skip if previously matched if (pas->qlMatchedSinks.contains(it.key())) { continue; } // check if the restore id matches. the only case where this would // happen is if the application was reopened during attenuation. if (pas->qhUnmatchedSinks.contains(it.value().stream_restore_id)) { PulseAttenuation active_sink = pas->qhUnmatchedSinks[it.value().stream_restore_id]; // if the volume wasn't changed from our attenuation if (pa_cvolume_equal(&active_sink.normal_volume, &it.value().attenuated_volume) != 0) { // reset the volume to normal pas->iRemainingOperations++; pa_operation_unref(pa_context_set_sink_input_volume(c, active_sink.index, &it.value().normal_volume, restore_volume_success_callback, pas)); } continue; } // at this point, we don't know what happened to the sink. add // it to a list to check the stream restore database for. pas->qhMissingSinks[it.value().stream_restore_id] = it.value(); } // clean up pas->qlMatchedSinks.clear(); pas->qhUnmatchedSinks.clear(); pas->qhVolumes.clear(); // if we had missing sinks, check the stream restore database // to see if we can find and update them. if (pas->qhMissingSinks.count() > 0) { pas->iRemainingOperations++; pa_operation_unref(pa_ext_stream_restore_read(c, stream_restore_read_callback, pas)); } // trigger the volume completion callback; // necessary so that shutdown actions are called restore_volume_success_callback(c, 1, pas); } }