mirror of
https://github.com/kavishdevar/librepods.git
synced 2026-01-28 22:01:50 +00:00
linux: add support for multiple media players (#222)
also add wait for setting card profiles to complete
This commit is contained in:
@@ -53,6 +53,7 @@ void MediaController::handleEarDetection(EarDetection *earDetection)
|
||||
{
|
||||
if (getCurrentMediaState() == Playing)
|
||||
{
|
||||
LOG_DEBUG("Pausing playback for ear detection");
|
||||
pause();
|
||||
}
|
||||
}
|
||||
@@ -64,7 +65,7 @@ void MediaController::handleEarDetection(EarDetection *earDetection)
|
||||
activateA2dpProfile();
|
||||
|
||||
// Resume if conditions are met and we previously paused
|
||||
if (shouldResume && wasPausedByApp && isActiveOutputDeviceAirPods())
|
||||
if (shouldResume && !pausedByAppServices.isEmpty() && isActiveOutputDeviceAirPods())
|
||||
{
|
||||
play();
|
||||
}
|
||||
@@ -211,6 +212,7 @@ void MediaController::activateA2dpProfile() {
|
||||
if (!m_pulseAudio->setCardProfile(m_deviceOutputName, preferredProfile)) {
|
||||
LOG_ERROR("Failed to activate A2DP profile: " << preferredProfile);
|
||||
}
|
||||
LOG_INFO("A2DP profile activated successfully");
|
||||
}
|
||||
|
||||
void MediaController::removeAudioOutputDevice() {
|
||||
@@ -248,31 +250,53 @@ MediaController::MediaState MediaController::getCurrentMediaState() const
|
||||
return mediaStateFromPlayerctlOutput(PlayerStatusWatcher::getCurrentPlaybackStatus(""));
|
||||
}
|
||||
|
||||
bool MediaController::sendMediaPlayerCommand(const QString &method)
|
||||
QStringList MediaController::getPlayingMediaPlayers()
|
||||
{
|
||||
// Connect to the session bus
|
||||
QStringList playingServices;
|
||||
QDBusConnection bus = QDBusConnection::sessionBus();
|
||||
|
||||
// Find available MPRIS-compatible media players
|
||||
QStringList services = bus.interface()->registeredServiceNames().value();
|
||||
QStringList mprisServices;
|
||||
for (const QString &service : services)
|
||||
{
|
||||
if (service.startsWith("org.mpris.MediaPlayer2."))
|
||||
if (!service.startsWith("org.mpris.MediaPlayer2."))
|
||||
{
|
||||
mprisServices << service;
|
||||
continue;
|
||||
}
|
||||
|
||||
QDBusInterface playerInterface(
|
||||
service,
|
||||
"/org/mpris/MediaPlayer2",
|
||||
"org.mpris.MediaPlayer2.Player",
|
||||
bus);
|
||||
|
||||
if (!playerInterface.isValid())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
QVariant playbackStatus = playerInterface.property("PlaybackStatus");
|
||||
if (playbackStatus.isValid() && playbackStatus.toString() == "Playing")
|
||||
{
|
||||
playingServices << service;
|
||||
LOG_DEBUG("Found playing service: " << service);
|
||||
}
|
||||
}
|
||||
|
||||
if (mprisServices.isEmpty())
|
||||
return playingServices;
|
||||
}
|
||||
|
||||
void MediaController::play()
|
||||
{
|
||||
if (pausedByAppServices.isEmpty())
|
||||
{
|
||||
LOG_ERROR("No MPRIS-compatible media players found on DBus");
|
||||
return false;
|
||||
LOG_INFO("No services to resume");
|
||||
return;
|
||||
}
|
||||
|
||||
bool success = false;
|
||||
// Try each MPRIS service until one succeeds
|
||||
for (const QString &service : mprisServices)
|
||||
QDBusConnection bus = QDBusConnection::sessionBus();
|
||||
int resumedCount = 0;
|
||||
|
||||
for (const QString &service : pausedByAppServices)
|
||||
{
|
||||
QDBusInterface playerInterface(
|
||||
service,
|
||||
@@ -282,63 +306,87 @@ bool MediaController::sendMediaPlayerCommand(const QString &method)
|
||||
|
||||
if (!playerInterface.isValid())
|
||||
{
|
||||
LOG_ERROR("Invalid DBus interface for service: " << service);
|
||||
LOG_WARN("Service no longer available: " << service);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Send the Play or Pause command
|
||||
if (method == "Play" || method == "Pause")
|
||||
QDBusReply<void> reply = playerInterface.call("Play");
|
||||
if (reply.isValid())
|
||||
{
|
||||
QDBusReply<void> reply = playerInterface.call(method);
|
||||
if (reply.isValid())
|
||||
{
|
||||
LOG_INFO("Successfully sent " << method << " to " << service);
|
||||
success = true;
|
||||
break; // Exit after the first successful command
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_ERROR("Failed to send " << method << " to " << service
|
||||
<< ": " << reply.error().message());
|
||||
}
|
||||
LOG_INFO("Resumed playback for: " << service);
|
||||
resumedCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_ERROR("Unsupported method: " << method);
|
||||
return false;
|
||||
LOG_ERROR("Failed to resume " << service << ": " << reply.error().message());
|
||||
}
|
||||
}
|
||||
|
||||
if (!success)
|
||||
if (resumedCount > 0)
|
||||
{
|
||||
LOG_ERROR("No media player responded successfully to " << method);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
void MediaController::play()
|
||||
{
|
||||
if (sendMediaPlayerCommand("Play"))
|
||||
{
|
||||
LOG_INFO("Resumed playback via DBus");
|
||||
wasPausedByApp = false;
|
||||
LOG_INFO("Resumed " << resumedCount << " media player(s) via DBus");
|
||||
pausedByAppServices.clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_ERROR("Failed to resume playback via DBus");
|
||||
LOG_ERROR("Failed to resume any media players via DBus");
|
||||
}
|
||||
}
|
||||
|
||||
void MediaController::pause()
|
||||
{
|
||||
if (sendMediaPlayerCommand("Pause"))
|
||||
QDBusConnection bus = QDBusConnection::sessionBus();
|
||||
QStringList services = bus.interface()->registeredServiceNames().value();
|
||||
|
||||
pausedByAppServices.clear();
|
||||
int pausedCount = 0;
|
||||
|
||||
for (const QString &service : services)
|
||||
{
|
||||
LOG_INFO("Paused playback via DBus");
|
||||
wasPausedByApp = true;
|
||||
if (!service.startsWith("org.mpris.MediaPlayer2."))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
QDBusInterface playerInterface(
|
||||
service,
|
||||
"/org/mpris/MediaPlayer2",
|
||||
"org.mpris.MediaPlayer2.Player",
|
||||
bus);
|
||||
|
||||
if (!playerInterface.isValid())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
QVariant playbackStatus = playerInterface.property("PlaybackStatus");
|
||||
LOG_DEBUG("PlaybackStatus for " << service << ": " << playbackStatus.toString());
|
||||
if (!playbackStatus.isValid() || playbackStatus.toString() != "Playing")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
QDBusReply<void> reply = playerInterface.call("Pause");
|
||||
LOG_DEBUG("Pausing service: " << service);
|
||||
if (reply.isValid())
|
||||
{
|
||||
LOG_INFO("Paused playback for: " << service);
|
||||
pausedByAppServices << service;
|
||||
pausedCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_ERROR("Failed to pause " << service << ": " << reply.error().message());
|
||||
}
|
||||
}
|
||||
|
||||
if (pausedCount > 0)
|
||||
{
|
||||
LOG_INFO("Paused " << pausedCount << " media player(s) via DBus");
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_ERROR("Failed to pause playback via DBus");
|
||||
LOG_INFO("No playing media players found to pause");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -55,9 +55,9 @@ Q_SIGNALS:
|
||||
private:
|
||||
MediaState mediaStateFromPlayerctlOutput(const QString &output) const;
|
||||
QString getAudioDeviceName();
|
||||
bool sendMediaPlayerCommand(const QString &method);
|
||||
QStringList getPlayingMediaPlayers();
|
||||
|
||||
bool wasPausedByApp = false;
|
||||
QStringList pausedByAppServices;
|
||||
int initialVolume = -1;
|
||||
QString connectedDeviceMacAddress;
|
||||
EarDetectionBehavior earDetectionBehavior = PauseWhenOneRemoved;
|
||||
|
||||
@@ -157,7 +157,14 @@ bool PulseAudioController::setSinkVolume(const QString &sinkName, int volumePerc
|
||||
pa_cvolume_set(&volume, 2, (volumePercent * PA_VOLUME_NORM) / 100);
|
||||
|
||||
pa_threaded_mainloop_lock(m_mainloop);
|
||||
pa_operation *op = pa_context_set_sink_volume_by_name(m_context, sinkName.toUtf8().constData(), &volume, nullptr, nullptr);
|
||||
|
||||
auto successCallback = [](pa_context *c, int success, void *userdata) {
|
||||
pa_threaded_mainloop *mainloop = static_cast<pa_threaded_mainloop*>(userdata);
|
||||
pa_threaded_mainloop_signal(mainloop, 0);
|
||||
};
|
||||
|
||||
pa_operation *op = pa_context_set_sink_volume_by_name(m_context, sinkName.toUtf8().constData(), &volume, successCallback, m_mainloop);
|
||||
|
||||
bool success = waitForOperation(op);
|
||||
if (op) pa_operation_unref(op);
|
||||
pa_threaded_mainloop_unlock(m_mainloop);
|
||||
@@ -170,10 +177,16 @@ bool PulseAudioController::setCardProfile(const QString &cardName, const QString
|
||||
if (!m_initialized) return false;
|
||||
|
||||
pa_threaded_mainloop_lock(m_mainloop);
|
||||
|
||||
auto successCallback = [](pa_context *c, int success, void *userdata) {
|
||||
pa_threaded_mainloop *mainloop = static_cast<pa_threaded_mainloop*>(userdata);
|
||||
pa_threaded_mainloop_signal(mainloop, 0);
|
||||
};
|
||||
|
||||
pa_operation *op = pa_context_set_card_profile_by_name(m_context,
|
||||
cardName.toUtf8().constData(),
|
||||
profileName.toUtf8().constData(),
|
||||
nullptr, nullptr);
|
||||
successCallback, m_mainloop);
|
||||
bool success = waitForOperation(op);
|
||||
if (op) pa_operation_unref(op);
|
||||
pa_threaded_mainloop_unlock(m_mainloop);
|
||||
|
||||
Reference in New Issue
Block a user