diff --git a/dist/changes/3.1.0.md b/dist/changes/3.1.0.md index 31f15f85..85857d44 100644 --- a/dist/changes/3.1.0.md +++ b/dist/changes/3.1.0.md @@ -12,6 +12,11 @@ - WControllerApplication: Update the extractMessage function. +## SkMedia + +- Create WVlcAudio. + + # Authors --- diff --git a/include/SkMedia/WVlcAudio b/include/SkMedia/WVlcAudio new file mode 100644 index 00000000..194f48dc --- /dev/null +++ b/include/SkMedia/WVlcAudio @@ -0,0 +1 @@ +#include "../../src/SkMedia/src/vlc/WVlcAudio.h" diff --git a/include/SkMedia/WVlcAudio.h b/include/SkMedia/WVlcAudio.h new file mode 100644 index 00000000..194f48dc --- /dev/null +++ b/include/SkMedia/WVlcAudio.h @@ -0,0 +1 @@ +#include "../../src/SkMedia/src/vlc/WVlcAudio.h" diff --git a/include/SkMedia/private/WVlcAudio_p b/include/SkMedia/private/WVlcAudio_p new file mode 100644 index 00000000..f582d589 --- /dev/null +++ b/include/SkMedia/private/WVlcAudio_p @@ -0,0 +1 @@ +#include "../../../src/SkMedia/src/vlc/WVlcAudio_p.h" diff --git a/include/SkMedia/private/WVlcAudio_p.h b/include/SkMedia/private/WVlcAudio_p.h new file mode 100644 index 00000000..f582d589 --- /dev/null +++ b/include/SkMedia/private/WVlcAudio_p.h @@ -0,0 +1 @@ +#include "../../../src/SkMedia/src/vlc/WVlcAudio_p.h" diff --git a/src/SkMedia/src/controllers/WControllerMedia.cpp b/src/SkMedia/src/controllers/WControllerMedia.cpp index 4f15ddc6..644ffc3c 100644 --- a/src/SkMedia/src/controllers/WControllerMedia.cpp +++ b/src/SkMedia/src/controllers/WControllerMedia.cpp @@ -45,6 +45,7 @@ #ifndef SK_NO_PLAYER #include #include +#include #endif W_INIT_CONTROLLER(WControllerMedia) @@ -2882,6 +2883,13 @@ WControllerMedia::WControllerMedia() : WController(new WControllerMediaPrivate(t return new WVlcPlayer(d->engine, d->thread); } +/* Q_INVOKABLE */ WVlcAudio * WControllerMedia::createVlcAudio() const +{ + Q_D(const WControllerMedia); + + return new WVlcAudio(d->engine, d->thread); +} + #endif //------------------------------------------------------------------------------------------------- diff --git a/src/SkMedia/src/controllers/WControllerMedia.h b/src/SkMedia/src/controllers/WControllerMedia.h index 100bc338..d5c0a441 100644 --- a/src/SkMedia/src/controllers/WControllerMedia.h +++ b/src/SkMedia/src/controllers/WControllerMedia.h @@ -44,6 +44,7 @@ class WYamlNode; #ifndef SK_NO_PLAYER class WVlcEngine; class WVlcPlayer; +class WVlcAudio; #endif #ifdef QT_6 @@ -288,6 +289,7 @@ class SK_MEDIA_EXPORT WControllerMedia : public WController public: // Interface #ifndef SK_NO_PLAYER Q_INVOKABLE WVlcPlayer * createVlcPlayer() const; + Q_INVOKABLE WVlcAudio * createVlcAudio () const; #endif Q_INVOKABLE diff --git a/src/SkMedia/src/vlc/WVlcAudio.cpp b/src/SkMedia/src/vlc/WVlcAudio.cpp new file mode 100644 index 00000000..a89e2ae8 --- /dev/null +++ b/src/SkMedia/src/vlc/WVlcAudio.cpp @@ -0,0 +1,696 @@ +//================================================================================================= +/* + Copyright (C) 2015-2020 Sky kit authors. + + Author: Benjamin Arnaud. + + This file is part of SkMedia. + + - GNU Lesser General Public License Usage: + This file may be used under the terms of the GNU Lesser General Public License version 3 as + published by the Free Software Foundation and appearing in the LICENSE.md file included in the + packaging of this file. Please review the following information to ensure the GNU Lesser + General Public License requirements will be met: https://www.gnu.org/licenses/lgpl.html. + + - Private License Usage: + Sky kit licensees holding valid private licenses may use this file in accordance with the + private license agreement provided with the Software or, alternatively, in accordance with the + terms contained in written agreement between you and Sky kit authors. For further information + contact us at contact@omega.gg. +*/ +//================================================================================================= + +#include "WVlcAudio.h" + +#ifndef SK_NO_VLCAUDIO + +// Qt includes +#include + +// Sk includes +#include +#include + +// Private includes +#include + +//------------------------------------------------------------------------------------------------- +// Static variables + +static const int AUDIO_RESYNCHRONIZE = 2000; // 2 seconds + +static const int AUDIO_DELAY = 1000; // 1 second + +//------------------------------------------------------------------------------------------------- +// Private +//------------------------------------------------------------------------------------------------- + +WVlcAudioPrivate::WVlcAudioPrivate(WVlcAudio * p) : WPrivate(p) {} + +void WVlcAudioPrivate::init(WVlcEngine * engine, QThread * thread) +{ + Q_Q(WVlcAudio); + + this->engine = engine; + this->thread = thread; + + player = NULL; + + playing = false; + buffering = false; + + wait = false; + + delay = -1; + + // FIXME: Should we set this to 1000 by default like VLC ? + networkCache = -1; + + timer.setInterval(AUDIO_DELAY); + + timer.setSingleShot(true); + + const QMetaObject * meta = q->metaObject(); + + method = meta->method(meta->indexOfMethod("onWait(bool)")); + + QObject::connect(&timer, SIGNAL(timeout()), q, SIGNAL(triggerPause())); + + if (thread == NULL) + { + create(); + + return; + } + + q->moveToThread(thread); + + timer.moveToThread(thread); + + QCoreApplication::postEvent(q, new QEvent(static_cast + (WVlcAudioPrivate::EventCreate)), + Qt::HighEventPriority * 100); +} + +//------------------------------------------------------------------------------------------------- +// Private functions +//------------------------------------------------------------------------------------------------- + +void WVlcAudioPrivate::create() +{ + player = libvlc_media_player_new(engine->d_func()->instance); + + libvlc_event_manager_t * manager = libvlc_media_player_event_manager(player); + + libvlc_event_attach(manager, libvlc_MediaPlayerPlaying, onPlaying, this); + libvlc_event_attach(manager, libvlc_MediaPlayerPaused, onPaused, this); + libvlc_event_attach(manager, libvlc_MediaPlayerStopped, onStopped, this); + + libvlc_event_attach(manager, libvlc_MediaPlayerTimeChanged, onTime, this); +} + +void WVlcAudioPrivate::setSource(const QString & url, int loop) +{ + if (player == NULL) return; + + QString cache; + + QString proxy; + QString proxyPassword; + + mutex.lock(); + + // NOTE: I'm not sure we need this anymore. + if (networkCache != -1) + { + cache = "network-caching=" + QString::number(networkCache); + } + + if (proxyHost.isEmpty() == false) + { + proxy = "http-proxy=" + proxyHost; + proxyPassword = "http-proxy-pwd=" + proxyPassword; + } + + mutex.unlock(); + + QString source = url; + + source.replace(" ", "%20"); + +#if LIBVLC_VERSION_MAJOR < 4 + libvlc_media_t * media = libvlc_media_new_location(engine->d_func()->instance, source.C_UTF); +#else + libvlc_media_t * media = libvlc_media_new_location(source.C_UTF); +#endif + + libvlc_media_add_option(media, "no-video"); + + if (loop) + { + // NOTE: We use the maximum value given we can't set an infinite value. + libvlc_media_add_option(media, "input-repeat=65535"); + } + + if (cache.isNull() == false) + { + libvlc_media_add_option(media, cache.C_STR); + } + + if (proxy.isNull() == false) + { + libvlc_media_add_option(media, proxy .C_STR); + libvlc_media_add_option(media, proxyPassword.C_STR); + } + + foreach (const QString & option, options) + { + libvlc_media_add_option(media, option.C_STR); + } + + libvlc_media_player_set_media(player, media); +} + +void WVlcAudioPrivate::play(int time) +{ + if (player == NULL) return; + + if (playing) + { + if (delay == -1) + { + applyTime(time); + + return; + } + + qint64 gap = libvlc_media_player_get_time(player) - time; + + if (delay == gap) return; + + if (qAbs(gap) > AUDIO_RESYNCHRONIZE) + { + applyTime(time); + + return; + } + + delay = gap; + + libvlc_audio_set_delay(player, gap * 1000); + + return; + } + + libvlc_state_t state = libvlc_media_player_get_state(player); + + if (state == libvlc_Opening) return; + + buffering = false; + + if (state == libvlc_Paused && delay == -1) + { + applyTime(time); + } + + libvlc_media_player_play(player); +} + +void WVlcAudioPrivate::pause() +{ + if (player == NULL || playing == false) return; + + playing = false; + + wait = false; + + libvlc_media_player_set_pause(player, 1); + + clearDelay(); +} + +void WVlcAudioPrivate::stop() +{ + if (player == NULL) return; + + playing = false; + buffering = false; + + wait = false; + +#if LIBVLC_VERSION_MAJOR < 4 + libvlc_media_player_stop(player); + + clearDelay(); +#else + libvlc_media_player_set_pause(player, 1); + + clearDelay(); + + libvlc_media_player_set_time(player, 0, false); +#endif +} + +void WVlcAudioPrivate::applyBuffering() +{ + if (playing) + { + playing = false; + + libvlc_media_player_set_pause(player, 1); + + clearDelay(); + } + else buffering = true; +} + +void WVlcAudioPrivate::setSpeed(qreal speed) +{ + if (player == NULL) return; + + libvlc_media_player_set_rate(player, speed); +} + +void WVlcAudioPrivate::setVolume(int percent) +{ + if (player == NULL) return; + + libvlc_audio_set_volume(player, percent); +} + +void WVlcAudioPrivate::deletePlayer() +{ + if (player == NULL) return; + + stop(); + + libvlc_media_player_release(player); +} + +//------------------------------------------------------------------------------------------------- + +void WVlcAudioPrivate::applyPlay() +{ + if (buffering) + { + buffering = false; + + libvlc_media_player_set_pause(player, 1); + } + else playing = true; +} + +void WVlcAudioPrivate::applyTime(int time) +{ + setWait(true); + + delay = 0; + +#if LIBVLC_VERSION_MAJOR < 4 + libvlc_media_player_set_time(player, time); +#else + libvlc_media_player_set_time(player, time, false); +#endif +} + +void WVlcAudioPrivate::clearDelay() +{ + if (delay == -1) return; + + delay = -1; + + libvlc_audio_set_delay(player, 0); +} + +void WVlcAudioPrivate::setWait(bool enabled) +{ + Q_Q(WVlcAudio); + + // NOTE Qt: QTimer(s) cannot be called from a different thread. + method.invoke(q, Qt::QueuedConnection, Q_ARG(bool, enabled)); +} + +//------------------------------------------------------------------------------------------------- +// Private static events +//------------------------------------------------------------------------------------------------- + +/* static */ void WVlcAudioPrivate::onPlaying(const struct libvlc_event_t *, void * data) +{ + WVlcAudioPrivate * d = static_cast (data); + + d->applyPlay(); +} + +/* static */ void WVlcAudioPrivate::onPaused(const struct libvlc_event_t *, void * data) +{ + WVlcAudioPrivate * d = static_cast (data); + + d->playing = false; +} + +/* static */ void WVlcAudioPrivate::onStopped(const struct libvlc_event_t *, void * data) +{ + WVlcAudioPrivate * d = static_cast (data); + + d->playing = false; + d->buffering = false; +} + +/* static */ void WVlcAudioPrivate::onTime(const struct libvlc_event_t *, void * data) +{ + WVlcAudioPrivate * d = static_cast (data); + +#if LIBVLC_VERSION_MAJOR > 3 + // FIXME VLC 4.0.0: Sometimes the playing event is not called. + if (d->playing == false && libvlc_media_player_get_state(d->player) == libvlc_Playing) + { + d->applyPlay(); + } +#endif + + d->setWait(false); +} + +//------------------------------------------------------------------------------------------------- +// Private slots +//------------------------------------------------------------------------------------------------- + +void WVlcAudioPrivate::onWait(bool enabled) +{ + QMutexLocker locker(&mutex); + + if (wait == enabled) return; + + Q_Q(WVlcAudio); + + wait = enabled; + + if (wait == false) + { + timer.stop(); + + emit q->triggerPlay(); + } + else timer.start(); + + emit q->waitingChanged(); +} + +//------------------------------------------------------------------------------------------------- +// Ctor / dtor +//------------------------------------------------------------------------------------------------- + +WVlcAudio::WVlcAudio(WVlcEngine * engine, QThread * thread, QObject * parent) + : QObject(parent), WPrivatable(new WVlcAudioPrivate(this)) +{ + Q_D(WVlcAudio); d->init(engine, thread); +} + +//------------------------------------------------------------------------------------------------- +// Interface +//------------------------------------------------------------------------------------------------- + +/* Q_INVOKABLE */ void WVlcAudio::setSource(const QString & url, int loop) +{ + Q_D(WVlcAudio); + + if (d->thread == NULL) + { + d->setSource(url, loop); + + return; + } + + QCoreApplication::postEvent(this, new WVlcAudioEventSource(url, loop)); +} + +/* Q_INVOKABLE */ void WVlcAudio::play(int time) +{ + Q_D(WVlcAudio); + + if (d->thread == NULL) + { + d->play(time); + + return; + } + + QCoreApplication::postEvent(this, new WVlcAudioPrivateEvent(WVlcAudioPrivate::EventPlay, + time)); +} + +/* Q_INVOKABLE */ void WVlcAudio::pause() +{ + Q_D(WVlcAudio); + + if (d->thread == NULL) + { + d->pause(); + + return; + } + + QCoreApplication::postEvent(this, new QEvent(static_cast + (WVlcAudioPrivate::EventPause))); +} + +/* Q_INVOKABLE */ void WVlcAudio::stop() +{ + Q_D(WVlcAudio); + + if (d->thread == NULL) + { + d->stop(); + + return; + } + + QCoreApplication::postEvent(this, new QEvent(static_cast + (WVlcAudioPrivate::EventStop))); +} + +/* Q_INVOKABLE */ void WVlcAudio::applyBuffering() +{ + Q_D(WVlcAudio); + + if (d->thread == NULL) + { + d->applyBuffering(); + + return; + } + + QCoreApplication::postEvent(this, new QEvent(static_cast + (WVlcAudioPrivate::EventBuffering))); +} + +/* Q_INVOKABLE */ void WVlcAudio::setSpeed(qreal speed) +{ + Q_D(WVlcAudio); + + if (d->thread == NULL) + { + d->setSpeed(speed); + + return; + } + + QCoreApplication::postEvent(this, new WVlcAudioPrivateEvent(WVlcAudioPrivate::EventSpeed, + speed)); +} + +/* Q_INVOKABLE */ void WVlcAudio::setVolume(int percent) +{ + Q_D(WVlcAudio); + + if (d->thread == NULL) + { + d->setVolume(percent); + + return; + } + + QCoreApplication::postEvent(this, new WVlcAudioPrivateEvent(WVlcAudioPrivate::EventVolume, + percent)); +} + +//------------------------------------------------------------------------------------------------- + +/* Q_INVOKABLE */ void WVlcAudio::setProxy(const QString & host, + int port, const QString & password) +{ + Q_D(WVlcAudio); + + d->mutex.lock(); + + d->proxyHost = host + ':' + QString::number(port); + d->proxyPassword = password; + + d->mutex.unlock(); +} + +/* Q_INVOKABLE */ void WVlcAudio::clearProxy() +{ + Q_D(WVlcAudio); + + d->mutex.lock(); + + d->proxyHost = QString(); + d->proxyPassword = QString(); + + d->mutex.unlock(); +} + +/* Q_INVOKABLE */ void WVlcAudio::deletePlayer() +{ + Q_D(WVlcAudio); + + if (d->thread == NULL) + { + d->deletePlayer(); + + return; + } + + QCoreApplication::postEvent(this, new QEvent(static_cast + (WVlcAudioPrivate::EventDelete)), + Qt::HighEventPriority * 100); +} + +//------------------------------------------------------------------------------------------------- +// Events +//------------------------------------------------------------------------------------------------- + +/* virtual */ bool WVlcAudio::event(QEvent * event) +{ + Q_D(WVlcAudio); + + QEvent::Type type = event->type(); + + if (type == static_cast (WVlcAudioPrivate::EventCreate)) + { + d->create(); + + return true; + } + else if (type == static_cast (WVlcAudioPrivate::EventSource)) + { + WVlcAudioEventSource * eventSource = static_cast (event); + + d->setSource(eventSource->url, eventSource->loop); + + return true; + } + else if (type == static_cast (WVlcAudioPrivate::EventPlay)) + { + WVlcAudioPrivateEvent * eventPlayer = static_cast (event); + + d->play(eventPlayer->value.toInt()); + + return true; + } + else if (type == static_cast (WVlcAudioPrivate::EventPause)) + { + d->pause(); + + return true; + } + else if (type == static_cast (WVlcAudioPrivate::EventStop)) + { + d->stop(); + + return true; + } + else if (type == static_cast (WVlcAudioPrivate::EventBuffering)) + { + d->applyBuffering(); + + return true; + } + else if (type == static_cast (WVlcAudioPrivate::EventSpeed)) + { + WVlcAudioPrivateEvent * eventPlayer = static_cast (event); + + d->setSpeed(eventPlayer->value.toInt()); + + return true; + } + else if (type == static_cast (WVlcAudioPrivate::EventVolume)) + { + WVlcAudioPrivateEvent * eventPlayer = static_cast (event); + + d->setVolume(eventPlayer->value.toInt()); + + return true; + } + else if (type == static_cast (WVlcAudioPrivate::EventDelete)) + { + d->deletePlayer(); + + return true; + } + else return QObject::event(event); +} + +//------------------------------------------------------------------------------------------------- +// Properties +//------------------------------------------------------------------------------------------------- + +bool WVlcAudio::isWaiting() +{ + Q_D(WVlcAudio); + + const QMutexLocker locker(&d->mutex); + + return d->wait; +} + +QStringList WVlcAudio::options() +{ + Q_D(WVlcAudio); + + const QMutexLocker locker(&d->mutex); + + return d->options; +} + +void WVlcAudio::setOptions(const QStringList & options) +{ + Q_D(WVlcAudio); + + QMutexLocker locker(&d->mutex); + + if (d->options == options) return; + + d->options = options; + + locker.unlock(); + + emit optionsChanged(); +} + +int WVlcAudio::networkCache() +{ + Q_D(WVlcAudio); + + QMutexLocker locker(&d->mutex); + + return d->networkCache; +} + +void WVlcAudio::setNetworkCache(int msec) +{ + Q_D(WVlcAudio); + + QMutexLocker locker(&d->mutex); + + if (d->networkCache == msec) return; + + d->networkCache = msec; + + locker.unlock(); + + emit networkCacheChanged(); +} + +#endif // SK_NO_VLCAUDIO diff --git a/src/SkMedia/src/vlc/WVlcAudio.h b/src/SkMedia/src/vlc/WVlcAudio.h new file mode 100644 index 00000000..eb5c2fcd --- /dev/null +++ b/src/SkMedia/src/vlc/WVlcAudio.h @@ -0,0 +1,112 @@ +//================================================================================================= +/* + Copyright (C) 2015-2020 Sky kit authors. + + Author: Benjamin Arnaud. + + This file is part of SkMedia. + + - GNU Lesser General Public License Usage: + This file may be used under the terms of the GNU Lesser General Public License version 3 as + published by the Free Software Foundation and appearing in the LICENSE.md file included in the + packaging of this file. Please review the following information to ensure the GNU Lesser + General Public License requirements will be met: https://www.gnu.org/licenses/lgpl.html. + + - Private License Usage: + Sky kit licensees holding valid private licenses may use this file in accordance with the + private license agreement provided with the Software or, alternatively, in accordance with the + terms contained in written agreement between you and Sky kit authors. For further information + contact us at contact@omega.gg. +*/ +//================================================================================================= + +#ifndef WVLCAUDIO_H +#define WVLCAUDIO_H + +// Qt includes +#include + +// FIXME MSVC: ssize_t is required by vlc headers. +#ifdef _MSC_VER +#include +typedef SSIZE_T ssize_t; +#endif + +// VLC includes +#include + +// Sk includes +#include + +#ifndef SK_NO_VLCAUDIO + +// Forward declarations +class WVlcAudioPrivate; +class WVlcEngine; + +class SK_MEDIA_EXPORT WVlcAudio : public QObject, public WPrivatable +{ + Q_OBJECT + + Q_PROPERTY(bool isWaiting READ isWaiting NOTIFY waitingChanged) + + Q_PROPERTY(QStringList options READ options WRITE setOptions NOTIFY optionsChanged) + + Q_PROPERTY(int networkCache READ networkCache WRITE setNetworkCache NOTIFY networkCacheChanged) + +public: + WVlcAudio(WVlcEngine * engine, QThread * thread = NULL, QObject * parent = NULL); + +public: // Interface + Q_INVOKABLE void setSource(const QString & url, int loop = false); + + Q_INVOKABLE void play(int time); + + Q_INVOKABLE void pause(); + Q_INVOKABLE void stop (); + + Q_INVOKABLE void applyBuffering(); + + Q_INVOKABLE void setSpeed(qreal speed); + + Q_INVOKABLE void setVolume(int percent); + + Q_INVOKABLE void setProxy(const QString & host, + int port, const QString & password = QString()); + + Q_INVOKABLE void clearProxy(); + + Q_INVOKABLE void deletePlayer(); + +protected: // Events + /* virtual */ bool event(QEvent * event); + +signals: + void triggerPlay (); + void triggerPause(); + + void waitingChanged(); + + void optionsChanged(); + + void networkCacheChanged(); + +public: // Properties + bool isWaiting(); + + QStringList options(); + void setOptions(const QStringList & options); + + int networkCache(); + void setNetworkCache(int msec); + +private: + W_DECLARE_PRIVATE(WVlcAudio) + + Q_PRIVATE_SLOT(d_func(), void onWait(bool)) +}; + +#include + +#endif // SK_NO_VLCAUDIO +#endif // WVLCAUDIO_H diff --git a/src/SkMedia/src/vlc/WVlcAudio_p.h b/src/SkMedia/src/vlc/WVlcAudio_p.h new file mode 100644 index 00000000..f4983cf7 --- /dev/null +++ b/src/SkMedia/src/vlc/WVlcAudio_p.h @@ -0,0 +1,178 @@ +//================================================================================================= +/* + Copyright (C) 2015-2020 Sky kit authors. + + Author: Benjamin Arnaud. + + This file is part of SkMedia. + + - GNU Lesser General Public License Usage: + This file may be used under the terms of the GNU Lesser General Public License version 3 as + published by the Free Software Foundation and appearing in the LICENSE.md file included in the + packaging of this file. Please review the following information to ensure the GNU Lesser + General Public License requirements will be met: https://www.gnu.org/licenses/lgpl.html. + + - Private License Usage: + Sky kit licensees holding valid private licenses may use this file in accordance with the + private license agreement provided with the Software or, alternatively, in accordance with the + terms contained in written agreement between you and Sky kit authors. For further information + contact us at contact@omega.gg. +*/ +//================================================================================================= + +#ifndef WVLCAUDIO_P_H +#define WVLCAUDIO_P_H + +/* W A R N I N G + ------------- + + This file is not part of the Sk API. It exists purely as an + implementation detail. This header file may change from version to + version without notice, or even be removed. + + We mean it. +*/ + +// Qt includes +#include +#include +#include +#include +#include + +// Private includes +#include + +#ifndef SK_NO_VLCAUDIO + +//------------------------------------------------------------------------------------------------- +// WVlcAudioPrivate +//------------------------------------------------------------------------------------------------- + +class SK_MEDIA_EXPORT WVlcAudioPrivate : public WPrivate +{ +public: // Enums + enum EventType + { + EventCreate = QEvent::User, + EventSource, + EventBuffering, + EventPlay, + EventPause, + EventStop, + EventSpeed, + EventVolume, + EventDelete + }; + +public: + WVlcAudioPrivate(WVlcAudio * p); + + void init(WVlcEngine * engine, QThread * thread); + +public: // Functions + void create(); + + void setSource(const QString & url, int loop); + + void play(int time); + + void pause(); + void stop (); + + void applyBuffering(); + + void setSpeed(qreal speed); + + void setVolume(int percent); + + void deletePlayer(); + + void applyPlay(); + + void applyTime(int time); + + void clearDelay(); + + void setWait(bool enabled); + +public: // Static events + static void onPlaying(const struct libvlc_event_t * event, void * data); + static void onPaused (const struct libvlc_event_t * event, void * data); + static void onStopped(const struct libvlc_event_t * event, void * data); + + static void onTime(const struct libvlc_event_t * event, void * data); + +public: // Slots + void onWait(bool enabled); + +public: // Variables + WVlcEngine * engine; + QThread * thread; + + libvlc_media_player_t * player; + + bool playing; + bool buffering; + + bool wait; + + qint64 delay; + + QStringList options; + + int networkCache; + + QString proxyHost; + QString proxyPassword; + + QTimer timer; + + QMetaMethod method; + + QMutex mutex; + +protected: + W_DECLARE_PUBLIC(WVlcAudio) +}; + +//------------------------------------------------------------------------------------------------- +// WVlcAudioPrivateEvent +//------------------------------------------------------------------------------------------------- + +class WVlcAudioPrivateEvent : public QEvent +{ +public: + WVlcAudioPrivateEvent(WVlcAudioPrivate::EventType type, const QVariant & value) + : QEvent(static_cast (type)) + { + this->value = value; + } + +public: // Variables + QVariant value; +}; + +//------------------------------------------------------------------------------------------------- +// WVlcAudioEventSource +//------------------------------------------------------------------------------------------------- + +class WVlcAudioEventSource : public QEvent +{ +public: + WVlcAudioEventSource(const QString & url, int loop) + : QEvent(static_cast (WVlcAudioPrivate::EventSource)) + { + this->url = url; + + this->loop = loop; + } + +public: // Variables + QString url; + + bool loop; +}; + +#endif // SK_NO_VLCAUDIO +#endif // WVLCAUDIO_P_H diff --git a/src/SkMedia/src/vlc/WVlcEngine.h b/src/SkMedia/src/vlc/WVlcEngine.h index 2ebc915b..eb0837c4 100644 --- a/src/SkMedia/src/vlc/WVlcEngine.h +++ b/src/SkMedia/src/vlc/WVlcEngine.h @@ -65,6 +65,8 @@ class SK_MEDIA_EXPORT WVlcEngine : public QObject, public WPrivatable friend class WVlcPlayer; friend class WVlcPlayerPrivate; + friend class WVlcAudio; + friend class WVlcAudioPrivate; }; #endif // SK_NO_VLCENGINE diff --git a/src/SkMedia/src/vlc/WVlcPlayer.cpp b/src/SkMedia/src/vlc/WVlcPlayer.cpp index ffdda86c..c0534702 100644 --- a/src/SkMedia/src/vlc/WVlcPlayer.cpp +++ b/src/SkMedia/src/vlc/WVlcPlayer.cpp @@ -28,233 +28,20 @@ #include #include -// Private includes -#include "WVlcPlayer_p.h" -#include - +// Sk includes #ifdef VLCPLAYER_AUDIO - -//------------------------------------------------------------------------------------------------- -// Static variables - -static const int PLAYER_RESYNCHRONIZE = 10000; // 10 seconds - -//================================================================================================= -// WVlcPlayerPrivateAudio -//================================================================================================= - -WVlcPlayerPrivateAudio::WVlcPlayerPrivateAudio(WVlcPlayerPrivate * p, libvlc_instance_t * instance) -{ - player = libvlc_media_player_new(instance); - - playing = false; - buffering = false; - - delay = -1; - - libvlc_event_manager_t * manager = libvlc_media_player_event_manager(player); - - libvlc_event_attach(manager, libvlc_MediaPlayerPlaying, onPlaying, this); - libvlc_event_attach(manager, libvlc_MediaPlayerPaused, onPaused, this); - libvlc_event_attach(manager, libvlc_MediaPlayerStopped, onStopped, this); - -#if LIBVLC_VERSION_MAJOR > 3 - libvlc_event_attach(manager, libvlc_MediaPlayerTimeChanged, onTime, this); -#endif - - libvlc_media_player_set_rate(player, libvlc_media_player_get_rate(p->player)); - - libvlc_audio_set_volume(player, libvlc_audio_get_volume(p->player)); -} - -/* virtual */ WVlcPlayerPrivateAudio::~WVlcPlayerPrivateAudio() -{ - libvlc_media_player_release(player); -} - -//------------------------------------------------------------------------------------------------- -// Interface -//------------------------------------------------------------------------------------------------- - -void WVlcPlayerPrivateAudio::pause() -{ - if (playing == false) return; - - playing = false; - - libvlc_media_player_set_pause(player, 1); - - clearDelay(); -} - -void WVlcPlayerPrivateAudio::stop() -{ - playing = false; - buffering = false; - -#if LIBVLC_VERSION_MAJOR < 4 - libvlc_media_player_stop(player); - - clearDelay(); -#else - libvlc_media_player_set_pause(player, 1); - - clearDelay(); - - libvlc_media_player_set_time(player, 0, false); +#include +#include #endif -} - -void WVlcPlayerPrivateAudio::setSpeed(float speed) -{ - libvlc_media_player_set_rate(player, speed); -} - -void WVlcPlayerPrivateAudio::setVolume(int volume) -{ - libvlc_audio_set_volume(player, volume); -} - -//------------------------------------------------------------------------------------------------- - -void WVlcPlayerPrivateAudio::applyBuffering() -{ - if (playing) - { - playing = false; - - libvlc_media_player_set_pause(player, 1); - - clearDelay(); - } - else buffering = true; -} - -void WVlcPlayerPrivateAudio::applyDelay(int time) -{ - if (playing) - { - if (delay == -1) - { - applyTime(time); +#include - return; - } - - qint64 gap = libvlc_media_player_get_time(player) - time; - - if (delay == gap) return; - - if (qAbs(gap) > PLAYER_RESYNCHRONIZE) - { - applyTime(time); - - return; - } - - delay = gap; - - libvlc_audio_set_delay(player, gap * 1000); - - return; - } - - libvlc_state_t state = libvlc_media_player_get_state(player); - - if (state == libvlc_Opening) return; - - buffering = false; - - if (state == libvlc_Paused) - { - if (delay == -1) applyTime(time); - } - - libvlc_media_player_play(player); -} - -//------------------------------------------------------------------------------------------------- -// Static events -//------------------------------------------------------------------------------------------------- - -/* static */ void WVlcPlayerPrivateAudio::onPlaying(const struct libvlc_event_t *, void * data) -{ - WVlcPlayerPrivateAudio * d = static_cast (data); - - d->applyPlay(); -} - -/* static */ void WVlcPlayerPrivateAudio::onPaused(const struct libvlc_event_t *, void * data) -{ - WVlcPlayerPrivateAudio * d = static_cast (data); - - d->playing = false; -} - -/* static */ void WVlcPlayerPrivateAudio::onStopped(const struct libvlc_event_t *, void * data) -{ - WVlcPlayerPrivateAudio * d = static_cast (data); - - d->playing = false; - d->buffering = false; -} - -#if LIBVLC_VERSION_MAJOR > 3 - -/* static */ void WVlcPlayerPrivateAudio::onTime(const struct libvlc_event_t *, void * data) -{ - WVlcPlayerPrivateAudio * d = static_cast (data); - - // FIXME VLC 4.0.0: Sometimes the playing event is not called. - if (d->playing == false && libvlc_media_player_get_state(d->player) == libvlc_Playing) - { - d->applyPlay(); - } -} - -#endif +// Private includes +#include //------------------------------------------------------------------------------------------------- -// Private functions +// Private //------------------------------------------------------------------------------------------------- -void WVlcPlayerPrivateAudio::applyPlay() -{ - if (buffering) - { - buffering = false; - - libvlc_media_player_set_pause(player, 1); - } - else playing = true; -} - -void WVlcPlayerPrivateAudio::applyTime(int time) -{ - delay = 0; - -#if LIBVLC_VERSION_MAJOR < 4 - libvlc_media_player_set_time(player, time); -#else - libvlc_media_player_set_time(player, time, false); -#endif -} - -void WVlcPlayerPrivateAudio::clearDelay() -{ - if (delay == -1) return; - - delay = -1; - - libvlc_audio_set_delay(player, 0); -} - -#endif - -//================================================================================================= -// WVlcPlayerPrivate -//================================================================================================= - WVlcPlayerPrivate::WVlcPlayerPrivate(WVlcPlayer * p) : WPrivate(p) {} void WVlcPlayerPrivate::init(WVlcEngine * engine, QThread * thread) @@ -637,7 +424,13 @@ libvlc_media_track_t * WVlcPlayerPrivate::getTrack(int id, libvlc_track_type_t t d->playing = false; #ifdef VLCPLAYER_AUDIO - if (d->hasAudio) d->audio->pause(); + if (d->hasAudio) + { + // NOTE: We avoid sending EventPaused when we're waiting for the audio. + if (d->audio->isWaiting()) return; + + d->audio->pause(); + } #endif if (d->backend == NULL) return; @@ -698,8 +491,7 @@ libvlc_media_track_t * WVlcPlayerPrivate::getTrack(int id, libvlc_track_type_t t //------------------------------------------------------------------------------------------------- -/* static */ void WVlcPlayerPrivate::onLengthChanged(const struct libvlc_event_t * event, - void * data) +/* static */ void WVlcPlayerPrivate::onLength(const struct libvlc_event_t * event, void * data) { WVlcPlayerPrivate * d = static_cast (data); @@ -715,8 +507,7 @@ libvlc_media_track_t * WVlcPlayerPrivate::getTrack(int id, libvlc_track_type_t t new WVlcPlayerEvent(WVlcPlayer::EventLengthChanged, length)); } -/* static */ void WVlcPlayerPrivate::onTimeChanged(const struct libvlc_event_t * event, - void * data) +/* static */ void WVlcPlayerPrivate::onTime(const struct libvlc_event_t * event, void * data) { WVlcPlayerPrivate * d = static_cast (data); @@ -733,7 +524,7 @@ libvlc_media_track_t * WVlcPlayerPrivate::getTrack(int id, libvlc_track_type_t t { int time = event->u.media_player_time_changed.new_time; - if (d->playing) d->audio->applyDelay(time); + if (d->playing) d->audio->play(time); if (d->backend == NULL) return; @@ -784,6 +575,23 @@ libvlc_media_track_t * WVlcPlayerPrivate::getTrack(int id, libvlc_track_type_t t // Private slots //------------------------------------------------------------------------------------------------- +void WVlcPlayerPrivate::onPlay() +{ + if (player) libvlc_media_player_play(player); +} + +void WVlcPlayerPrivate::onPause() +{ + if (player == NULL) return; + + libvlc_media_player_set_pause(player, 1); + + if (backend == NULL) return; + + QCoreApplication::postEvent(backend, new QEvent(static_cast + (WVlcPlayer::EventBuffering))); +} + void WVlcPlayerPrivate::onOutputAdded(const WBackendOutput & output) { QList outputs; @@ -805,9 +613,9 @@ void WVlcPlayerPrivate::onOutputCleared() (WVlcPlayer::EventOutputClear))); } -//================================================================================================= -// WVlcPlayer -//================================================================================================= +//------------------------------------------------------------------------------------------------- +// Ctor / dtor +//------------------------------------------------------------------------------------------------- WVlcPlayer::WVlcPlayer(WVlcEngine * engine, QThread * thread, QObject * parent) : QObject(parent), WPrivatable(new WVlcPlayerPrivate(this)) @@ -975,8 +783,8 @@ WVlcPlayer::WVlcPlayer(WVlcEngine * engine, QThread * thread, QObject * parent) libvlc_event_attach(manager, libvlc_MediaPlayerBuffering, d->onBuffering, d); - libvlc_event_attach(manager, libvlc_MediaPlayerLengthChanged, d->onLengthChanged, d); - libvlc_event_attach(manager, libvlc_MediaPlayerTimeChanged, d->onTimeChanged, d); + libvlc_event_attach(manager, libvlc_MediaPlayerLengthChanged, d->onLength, d); + libvlc_event_attach(manager, libvlc_MediaPlayerTimeChanged, d->onTime, d); #if LIBVLC_VERSION_MAJOR < 4 libvlc_event_attach(manager, libvlc_MediaPlayerEndReached, d->onEndReached, d); @@ -1023,6 +831,8 @@ WVlcPlayer::WVlcPlayer(WVlcEngine * engine, QThread * thread, QObject * parent) QString proxy; QString proxyPassword; + QStringList options; + d->mutex.lock(); output = d->output; @@ -1039,6 +849,8 @@ WVlcPlayer::WVlcPlayer(WVlcEngine * engine, QThread * thread, QObject * parent) proxyPassword = "http-proxy-pwd=" + d->proxyPassword; } + options = d->options; + d->mutex.unlock(); libvlc_media_t * media; @@ -1097,77 +909,47 @@ WVlcPlayer::WVlcPlayer(WVlcEngine * engine, QThread * thread, QObject * parent) if (d->audio == NULL) { - d->audio = new WVlcPlayerPrivateAudio(d, - d->engine->d_func()->instance); - } + d->audio = new WVlcAudio(d->engine); - libvlc_media_t * mediaAudio = d->createMedia(audio); + d->audio->setSpeed(libvlc_media_player_get_rate(d->player)); - libvlc_media_add_option(media, "no-audio"); - libvlc_media_add_option(mediaAudio, "no-video"); + d->audio->setVolume(libvlc_audio_get_volume(d->player)); - if (eventSource->loop) - { - const char * string = "input-repeat=65535"; - - // NOTE: We use the maximum value given we can't set an infinite value. - libvlc_media_add_option(media, string); - libvlc_media_add_option(mediaAudio, string); - } + d->mutex.lock(); - if (cache.isNull() == false) - { - const char * string = cache.C_STR; + // FIXME + //d->audio->setProxy - libvlc_media_add_option(media, string); - libvlc_media_add_option(mediaAudio, string); - } + d->audio->setOptions(options); - if (proxy.isNull() == false) - { - const char * stringA = proxy .C_STR; - const char * stringB = proxyPassword.C_STR; + d->audio->setNetworkCache(d->networkCache); - libvlc_media_add_option(media, stringA); - libvlc_media_add_option(media, stringB); + d->mutex.unlock(); - libvlc_media_add_option(mediaAudio, stringA); - libvlc_media_add_option(mediaAudio, stringB); + connect(d->audio, SIGNAL(triggerPlay ()), this, SLOT(onPlay ())); + connect(d->audio, SIGNAL(triggerPause()), this, SLOT(onPause())); } - foreach (const QString & option, d->options) + d->audio->setSource(audio, eventSource->loop); + } + else + { + if (d->hasAudio) { - const char * string = option.C_STR; + d->hasAudio = false; - libvlc_media_add_option(media, string); - libvlc_media_add_option(mediaAudio, string); + d->audio->stop(); } - -#if LIBVLC_VERSION_MAJOR > 3 - d->opening = true; #endif - libvlc_media_player_set_media(d->player, media); - libvlc_media_player_set_media(d->audio->player, mediaAudio); + QString input = "input-slave=" + d->encodeUrl(audio); - return true; - } + libvlc_media_add_option(media, input.C_UTF); - if (d->hasAudio) - { - d->hasAudio = false; - - d->audio->stop(); + // FIXME VLC 3.0.18: This seems to fix the missing audio bytes at the end of + // the video. + libvlc_media_add_option(media, "demux=avformat"); } -#endif - - QString input = "input-slave=" + d->encodeUrl(audio); - - libvlc_media_add_option(media, input.C_UTF); - - // FIXME VLC 3.0.18: This seems to fix the missing audio bytes at the end of - // the video. - libvlc_media_add_option(media, "demux=avformat"); } #ifdef VLCPLAYER_AUDIO else if (d->hasAudio) @@ -1203,7 +985,7 @@ WVlcPlayer::WVlcPlayer(WVlcEngine * engine, QThread * thread, QObject * parent) libvlc_media_add_option(media, proxyPassword.C_STR); } - foreach (const QString & option, d->options) + foreach (const QString & option, options) { libvlc_media_add_option(media, option.C_STR); } diff --git a/src/SkMedia/src/vlc/WVlcPlayer.h b/src/SkMedia/src/vlc/WVlcPlayer.h index d30fc8b1..bc7888c0 100644 --- a/src/SkMedia/src/vlc/WVlcPlayer.h +++ b/src/SkMedia/src/vlc/WVlcPlayer.h @@ -38,12 +38,18 @@ typedef SSIZE_T ssize_t; #include // Sk includes -#include #include +// Defines +#if LIBVLC_VERSION_MAJOR > 3 +#define VLCPLAYER_AUDIO +#endif + #ifndef SK_NO_VLCPLAYER +// Forward declarations class WVlcPlayerPrivate; +class WVlcEngine; //------------------------------------------------------------------------------------------------- // WVlcPlayer @@ -142,6 +148,11 @@ class SK_MEDIA_EXPORT WVlcPlayer : public QObject, public WPrivatable private: W_DECLARE_PRIVATE(WVlcPlayer) +#ifdef VLCPLAYER_AUDIO + Q_PRIVATE_SLOT(d_func(), void onPlay ()) + Q_PRIVATE_SLOT(d_func(), void onPause()) +#endif + friend class WBackendVlc; friend class WBackendVlcPrivate; }; @@ -203,5 +214,7 @@ class WVlcOutputsEvent : public QEvent QList outputs; }; +#include + #endif // SK_NO_VLCPLAYER #endif // WVLCPLAYER_H diff --git a/src/SkMedia/src/vlc/WVlcPlayer_p.h b/src/SkMedia/src/vlc/WVlcPlayer_p.h index 6b80c076..85dfb4b4 100644 --- a/src/SkMedia/src/vlc/WVlcPlayer_p.h +++ b/src/SkMedia/src/vlc/WVlcPlayer_p.h @@ -35,6 +35,9 @@ // Qt includes #include +#ifdef VLCPLAYER_AUDIO +#include +#endif // Private includes #include @@ -43,10 +46,8 @@ // Forward declarations class WVlcPlayerPrivateAudio; - -// Defines -#if LIBVLC_VERSION_MAJOR > 3 -#define VLCPLAYER_AUDIO +#ifdef VLCPLAYER_AUDIO +class WVlcAudio; #endif //------------------------------------------------------------------------------------------------- @@ -118,8 +119,8 @@ class SK_MEDIA_EXPORT WVlcPlayerPrivate : public WPrivate static void onBuffering(const struct libvlc_event_t * event, void * data); - static void onLengthChanged(const struct libvlc_event_t * event, void * data); - static void onTimeChanged (const struct libvlc_event_t * event, void * data); + static void onLength(const struct libvlc_event_t * event, void * data); + static void onTime (const struct libvlc_event_t * event, void * data); #if LIBVLC_VERSION_MAJOR < 4 static void onEndReached(const struct libvlc_event_t * event, void * data); @@ -128,6 +129,9 @@ class SK_MEDIA_EXPORT WVlcPlayerPrivate : public WPrivate static void onEncounteredError(const struct libvlc_event_t * event, void * data); public: // Slots + void onPlay (); + void onPause(); + void onOutputAdded(const WBackendOutput & output); void onOutputRemoved(int index); @@ -135,14 +139,12 @@ class SK_MEDIA_EXPORT WVlcPlayerPrivate : public WPrivate void onOutputCleared(); public: // Variables - QMutex mutex; - WVlcEngine * engine; libvlc_media_player_t * player; #ifdef VLCPLAYER_AUDIO - WVlcPlayerPrivateAudio * audio; + WVlcAudio * audio; #endif QObject * backend; @@ -175,62 +177,12 @@ class SK_MEDIA_EXPORT WVlcPlayerPrivate : public WPrivate QString proxyHost; QString proxyPassword; + QMutex mutex; + protected: W_DECLARE_PUBLIC(WVlcPlayer) }; -#ifdef VLCPLAYER_AUDIO - -//------------------------------------------------------------------------------------------------- -// WVlcPlayerPrivateAudio -//------------------------------------------------------------------------------------------------- - -class WVlcPlayerPrivateAudio -{ -public: - WVlcPlayerPrivateAudio(WVlcPlayerPrivate * p, libvlc_instance_t * instance); - - virtual ~WVlcPlayerPrivateAudio(); - -public: // Interface - void pause(); - void stop (); - - void setSpeed(float speed); - - void setVolume(int volume); - - void applyBuffering(); - - void applyDelay(int time); - -public: // Static events - static void onPlaying(const struct libvlc_event_t * event, void * data); - static void onPaused (const struct libvlc_event_t * event, void * data); - static void onStopped(const struct libvlc_event_t * event, void * data); - -#if LIBVLC_VERSION_MAJOR > 3 - static void onTime(const struct libvlc_event_t * event, void * data); -#endif - -private: // Functions - void applyPlay(); - - void applyTime(int time); - - void clearDelay(); - -public: // Variables - libvlc_media_player_t * player; - - bool playing; - bool buffering; - - qint64 delay; -}; - -#endif - //------------------------------------------------------------------------------------------------- // WVlcPlayerPrivateEvent //------------------------------------------------------------------------------------------------- diff --git a/src/SkMedia/src/vlc/vlc.pri b/src/SkMedia/src/vlc/vlc.pri index e5f0c902..4840573a 100644 --- a/src/SkMedia/src/vlc/vlc.pri +++ b/src/SkMedia/src/vlc/vlc.pri @@ -4,6 +4,8 @@ HEADERS += src/vlc/WVlcEngine.h \ src/vlc/WVlcEngine_p.h \ src/vlc/WVlcPlayer.h \ src/vlc/WVlcPlayer_p.h \ + src/vlc/WVlcAudio.h \ + src/vlc/WVlcAudio_p.h \ SOURCES += src/vlc/WVlcEngine.cpp \ - src/vlc/WVlcPlayer.cpp \ + src/vlc/WVlcAudio.cpp \