diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -139,7 +139,7 @@ message(WARNING "UPnP support is experimental and may not work.") endif() -find_package(LIBVLC QUIET) +find_package(LIBVLC "3.0.0" QUIET) set_package_properties(LIBVLC PROPERTIES DESCRIPTION "libvlc allows to play music in Elisa (otherwise it will use QtMultimedia)" URL "https://www.videolan.org/vlc/libvlc.html" diff --git a/src/audiowrapper.h b/src/audiowrapper.h --- a/src/audiowrapper.h +++ b/src/audiowrapper.h @@ -78,6 +78,10 @@ WRITE setAudioRole NOTIFY audioRoleChanged) + Q_PROPERTY(QList externalRenderers + READ externalRenderers + NOTIFY externalRenderersChanged) + public: explicit AudioWrapper(QObject *parent = nullptr); @@ -104,6 +108,8 @@ QAudio::Role audioRole() const; + QList externalRenderers() const; + Q_SIGNALS: void mutedChanged(bool muted); @@ -134,6 +140,8 @@ void audioRoleChanged(); + void externalRenderersChanged(); + public Q_SLOTS: void setMuted(bool muted); @@ -187,6 +195,8 @@ void playerSeekableSignalChanges(bool isSeekable); + void externalRenderersSignalChanges(); + friend class AudioWrapperPrivate; std::unique_ptr d; diff --git a/src/audiowrapper_libvlc.cpp b/src/audiowrapper_libvlc.cpp --- a/src/audiowrapper_libvlc.cpp +++ b/src/audiowrapper_libvlc.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #if defined Q_OS_WIN @@ -44,14 +45,22 @@ PowerManagementInterface mPowerInterface; + QHash mExternalRenderers; + + QHash mExternalRenderersType; + AudioWrapper *mParent = nullptr; libvlc_instance_t *mInstance = nullptr; libvlc_media_player_t *mPlayer = nullptr; libvlc_event_manager_t *mPlayerEventManager = nullptr; + std::vector mRendererDiscoverers; + + std::vector mRendererDiscovererEventManagers; + libvlc_media_t *mMedia = nullptr; qint64 mMediaDuration = 0; @@ -133,6 +142,30 @@ libvlc_event_attach(d->mPlayerEventManager, libvlc_MediaPlayerUnmuted, &vlc_callback, d.get()); libvlc_event_attach(d->mPlayerEventManager, libvlc_MediaPlayerAudioVolume, &vlc_callback, d.get()); libvlc_event_attach(d->mPlayerEventManager, libvlc_MediaPlayerAudioDevice, &vlc_callback, d.get()); + + libvlc_rd_description_t **rendererTypeList = nullptr; + + auto nbRendererTypes = libvlc_renderer_discoverer_list_get(d->mInstance, &rendererTypeList); + + for (unsigned int rendererTypeIndex = 0; rendererTypeIndex < nbRendererTypes; ++rendererTypeIndex) { + qDebug() << "activate renderer discovery for type" << rendererTypeList[rendererTypeIndex]->psz_longname; + auto newRenderer = libvlc_renderer_discoverer_new(d->mInstance, rendererTypeList[rendererTypeIndex]->psz_name); + if (newRenderer) { + d->mRendererDiscoverers.push_back(newRenderer); + + auto newRendererEventManager = libvlc_renderer_discoverer_event_manager(newRenderer); + if (newRendererEventManager) { + d->mRendererDiscovererEventManagers.push_back(newRendererEventManager); + + libvlc_event_attach(newRendererEventManager, libvlc_RendererDiscovererItemAdded, &vlc_callback, d.get()); + libvlc_event_attach(newRendererEventManager, libvlc_RendererDiscovererItemDeleted, &vlc_callback, d.get()); + + libvlc_renderer_discoverer_start(newRenderer); + } + } + } + + libvlc_renderer_discoverer_list_release(rendererTypeList, nbRendererTypes); } AudioWrapper::~AudioWrapper() @@ -202,6 +235,11 @@ return {}/*d->mPlayer.audioRole()*/; } +QList AudioWrapper::externalRenderers() const +{ + return d->mExternalRenderers.keys(); +} + QMediaPlayer::State AudioWrapper::playbackState() const { return d->mPreviousPlayerState; @@ -259,6 +297,10 @@ libvlc_media_player_set_media(d->mPlayer, d->mMedia); + if (!d->mExternalRenderers.isEmpty()) { + libvlc_media_player_set_renderer(d->mPlayer, d->mExternalRenderers.begin().value()); + } + if (d->signalPlaybackChange(QMediaPlayer::StoppedState)) { Q_EMIT stopped(); } @@ -413,6 +455,11 @@ QMetaObject::invokeMethod(this, [this, isSeekable]() {Q_EMIT seekableChanged(isSeekable);}, Qt::QueuedConnection); } +void AudioWrapper::externalRenderersSignalChanges() +{ + QMetaObject::invokeMethod(this, [this]() {Q_EMIT externalRenderersChanged();}, Qt::QueuedConnection); +} + void AudioWrapperPrivate::vlcEventCallback(const struct libvlc_event_t *p_event) { const auto eventType = static_cast(p_event->type); @@ -482,6 +529,28 @@ break; case libvlc_MediaPlayerAudioDevice: qCDebug(orgKdeElisaPlayerVlc) << "AudioWrapperPrivate::vlcEventCallback" << "libvlc_MediaPlayerAudioDevice"; + break; + case libvlc_RendererDiscovererItemAdded: + qDebug() << "AudioWrapperPrivate::vlcEventCallback" << "libvlc_RendererDiscovererItemAdded"; + libvlc_renderer_item_hold(p_event->u.renderer_discoverer_item_added.item); + + mExternalRenderers[QString::fromLatin1(libvlc_renderer_item_name(p_event->u.renderer_discoverer_item_added.item))] = + p_event->u.renderer_discoverer_item_added.item; + mExternalRenderersType[QString::fromLatin1(libvlc_renderer_item_name(p_event->u.renderer_discoverer_item_added.item))] = + QString::fromLatin1(libvlc_renderer_item_type(p_event->u.renderer_discoverer_item_added.item)); + + mParent->externalRenderersSignalChanges(); + + break; + case libvlc_RendererDiscovererItemDeleted: + qDebug() << "AudioWrapperPrivate::vlcEventCallback" << "libvlc_RendererDiscovererItemDeleted"; + mExternalRenderers.remove(QString::fromLatin1(libvlc_renderer_item_name(p_event->u.renderer_discoverer_item_deleted.item))); + mExternalRenderersType.remove(QString::fromLatin1(libvlc_renderer_item_name(p_event->u.renderer_discoverer_item_deleted.item))); + + mParent->externalRenderersSignalChanges(); + + libvlc_renderer_item_hold(p_event->u.renderer_discoverer_item_deleted.item); + break; default: qCDebug(orgKdeElisaPlayerVlc) << "AudioWrapperPrivate::vlcEventCallback" << "eventType" << eventType;