#include <QOpenGLContext>
#include <QOpenGLFramebufferObject>
+#include <QString>
#include <QUrl>
#include <mpv/client.h>
qCDebug(mpvBackend) << "register" << VOLUME
<< mpv_observe_property(m_handle, 0, VOLUME,
MPV_FORMAT_DOUBLE);
+ qCDebug(mpvBackend) << "register track-list"
+ << mpv_observe_property(m_handle, 0, "track-list",
+ MPV_FORMAT_NODE);
+ qCDebug(mpvBackend) << "register vid"
+ << mpv_observe_property(m_handle, 0, "vid",
+ MPV_FORMAT_STRING);
+ qCDebug(mpvBackend) << "register aid"
+ << mpv_observe_property(m_handle, 0, "aid",
+ MPV_FORMAT_STRING);
+ qCDebug(mpvBackend) << "register sid"
+ << mpv_observe_property(m_handle, 0, "sid",
+ MPV_FORMAT_STRING);
qCDebug(mpvBackend) << "register chapter-list"
<< mpv_observe_property(m_handle, 0, "chapter-list",
MPV_FORMAT_NODE);
}
}
+void MpvInstance::setCurrentVideoStream(TrackIndex track) {
+ qint64 tmp = track;
+ if (track < 0)
+ mpv_set_property_string(m_handle, "vid", "no");
+ else
+ mpv_set_property(m_handle, "vid", MPV_FORMAT_INT64, &tmp);
+}
+
+void MpvInstance::setCurrentAudioStream(TrackIndex track) {
+ qint64 tmp = track;
+ if (track < 0)
+ mpv_set_property_string(m_handle, "aid", "no");
+ else
+ mpv_set_property(m_handle, "aid", MPV_FORMAT_INT64, &tmp);
+}
+
+void MpvInstance::setCurrentSubtitleStream(TrackIndex track) {
+ qint64 tmp = track;
+ if (track < 0)
+ mpv_set_property_string(m_handle, "sid", "no");
+ else
+ mpv_set_property(m_handle, "sid", MPV_FORMAT_INT64, &tmp);
+}
+
template <int type> struct MpvProperty;
template <> struct MpvProperty<MPV_FORMAT_NONE> {
}
};
+template <> struct MpvProperty<MPV_FORMAT_STRING> {
+ static char *read(struct mpv_event_property *property) {
+ Q_ASSERT(property->format == MPV_FORMAT_STRING);
+ if (!property->data)
+ qWarning("Property data data is null");
+ return *static_cast<char **>(property->data);
+ }
+};
+
template <int TYPE>
decltype(auto) readProperty(struct mpv_event_property *property) {
return MpvProperty<TYPE>::read(property);
}
+int MpvInstance::readTrackIndex(struct mpv_event_property *property) {
+ const auto str = readProperty<MPV_FORMAT_STRING>(property);
+ qCDebug(mpvBackend).nospace() << "Trying to read id from >" << str << "<";
+
+ const auto qstr = QString::fromUtf8(str);
+ bool ok = false;
+ const int id = qstr.toInt(&ok);
+ return ok ? id : NoTrack;
+}
+
void MpvInstance::processMpvEvents() {
struct mpv_event *event = nullptr;
do {
static_cast<PlayerPluginInterface::Volume>(
readProperty<MPV_FORMAT_DOUBLE>(property) / 100.0));
}
+ } else if (strcmp(property->name, "track-list") == 0) {
+ const auto node = readProperty<MPV_FORMAT_NODE>(property);
+ const auto variant = mpv::qt::node_to_variant(node);
+ qCDebug(mpvBackend) << "TRACKS" << variant;
+
+ PlayerPluginInterface::TrackList videoTracks;
+ PlayerPluginInterface::TrackList audioTracks;
+ PlayerPluginInterface::TrackList subtitleTracks;
+ for (const auto &v : variant.toList()) {
+ const auto &map = v.toMap();
+ const auto &type = map["type"].toString();
+ auto &tracks = [&]() -> PlayerPluginInterface::TrackList & {
+ if (type == "video") {
+ return videoTracks;
+ }
+ if (type == "audio") {
+ return audioTracks;
+ }
+ return subtitleTracks;
+ }();
+ const auto track = PlayerPluginInterface::Track{
+ map["title"].toString(), map["lang"].toString(),
+ map["id"].toInt()};
+ tracks << track;
+ }
+ m_player->backendVideoTracksChanged(videoTracks);
+ m_player->backendAudioTracksChanged(audioTracks);
+ m_player->backendSubtitleTracksChanged(subtitleTracks);
+
+ } else if (strcmp(property->name, "vid") == 0) {
+ const int trackIndex = readTrackIndex(property);
+ m_player->backendCurrentVideoTrackChanged(trackIndex);
+
+ } else if (strcmp(property->name, "aid") == 0) {
+ const int trackIndex = readTrackIndex(property);
+ m_player->backendCurrentAudioTrackChanged(trackIndex);
+
+ } else if (strcmp(property->name, "sid") == 0) {
+ const int trackIndex = readTrackIndex(property);
+ m_player->backendCurrentSubtitleTrackChanged(trackIndex);
+
} else if (strcmp(property->name, "chapter-list") == 0) {
const auto node = readProperty<MPV_FORMAT_NODE>(property);
const auto variant = mpv::qt::node_to_variant(node);
}
#ifdef Q_OS_WIN
-#include <windows.h>
+#include <Windows.h>
#endif
void *VideoRendererMpv::getProcAddress(void *, const char *name) {
struct mpv_handle;
struct mpv_opengl_cb_context;
+struct mpv_event_property;
class BACKEND_MPVSHARED_EXPORT BackendMpv : public QObject,
public BackendPluginBase {
class BACKEND_MPVSHARED_EXPORT MpvInstance : public QObject,
public BackendInstance {
Q_OBJECT
+
+ static constexpr const TrackIndex DefaultTrack = -2;
+ static constexpr const TrackIndex NoTrack = -1;
+
public:
MpvInstance(PlayerPluginInterface *, QObject *parent = nullptr);
virtual ~MpvInstance() override;
void setVolume(Volume) override;
+ void setCurrentVideoStream(TrackIndex) override;
+ void setCurrentAudioStream(TrackIndex) override;
+ void setCurrentSubtitleStream(TrackIndex) override;
+
private:
+ int readTrackIndex(struct mpv_event_property *);
+
PlayerPluginInterface *m_player = nullptr;
mpv_handle *m_handle = nullptr;
Volume m_volumeToSet = -1;
void NullInstance::seek(TimeStamp) {}
void NullInstance::setVolume(Volume) {}
+
+void NullInstance::setCurrentVideoStream(BackendInstance::TrackIndex)
+{
+
+}
+
+void NullInstance::setCurrentAudioStream(BackendInstance::TrackIndex)
+{
+
+}
+
+void NullInstance::setCurrentSubtitleStream(BackendInstance::TrackIndex)
+{
+
+}
void setVolume(Volume) override;
+ void setCurrentVideoStream(TrackIndex) override;
+ void setCurrentAudioStream(TrackIndex) override;
+ void setCurrentSubtitleStream(TrackIndex) override;
+
+
VideoRendererBase *createRenderer(VideoUpdateInterface *) override;
private:
#ifndef CHAPTERMODEL_H
#define CHAPTERMODEL_H
-#include "aniplayer/playerplugininterface.h"
#include <QAbstractListModel>
+#include "aniplayer/playerplugininterface.h"
+
class ChapterModel : public QAbstractListModel {
public:
enum ChapterRoles {
EndTimeRole
};
- ChapterModel(QObject *parent = nullptr);
+ explicit ChapterModel(QObject *parent = nullptr);
void setChapters(const PlayerPluginInterface::ChapterList &);
void setDuration(PlayerPluginInterface::TimeStamp duration);
videoelement.cpp \
instancemanager.cpp \
settings.cpp \
+ trackmodel.cpp \
chaptermodel.cpp
HEADERS += \
videoelement.h \
instancemanager.h \
settings.h \
+ trackmodel.h \
chaptermodel.h
include(qtsingleapplication/qtsingleapplication.pri)
class BackendInstance {
public:
+ using TrackIndex = int;
// In seconds
using TimeStamp = double;
// Volume valid range is 0.0-1.0
virtual void seek(TimeStamp) = 0;
virtual void setVolume(Volume) = 0;
+
+ virtual void setCurrentVideoStream(TrackIndex) = 0;
+ virtual void setCurrentAudioStream(TrackIndex) = 0;
+ virtual void setCurrentSubtitleStream(TrackIndex) = 0;
};
class BackendPluginBase {
--- /dev/null
+#ifndef BACKENDPLUGINBASE_H
+#define BACKENDPLUGINBASE_H
+
+#include "playerplugininterface.h"
+
+class QUrl;
+
+class BackendInstance {
+public:
+ using TrackIndex = int;
+ // In seconds
+ using TimeStamp = double;
+ // Volume valid range is 0.0-1.0
+ using Volume = double;
+ virtual ~BackendInstance() = default;
+
+ virtual VideoRendererBase *createRenderer(VideoUpdateInterface *) = 0;
+
+ virtual bool open(const QUrl &resource) = 0;
+
+ virtual void play() = 0;
+ virtual void pause() = 0;
+ virtual void stop() = 0;
+
+ virtual void seek(TimeStamp) = 0;
+
+ virtual void setVolume(Volume) = 0;
+
+ virtual void setCurrentVideoStream(TrackIndex) = 0;
+ virtual void setCurrentAudioStream(TrackIndex) = 0;
+ virtual void setCurrentSubtitleStream(TrackIndex) = 0;
+};
+
+class BackendPluginBase {
+public:
+ virtual ~BackendPluginBase() = default;
+
+ virtual BackendInstance *createInstance(PlayerPluginInterface *) = 0;
+};
+
+#define ANIPLAYER_BACKEND_DPLUGIN_INTERFACE_IID \
+ "org.aptx.aniplayer.BackendPluginInterface"
+
+#include <QObject>
+Q_DECLARE_INTERFACE(BackendPluginBase, ANIPLAYER_BACKEND_DPLUGIN_INTERFACE_IID)
+
+#endif // BACKENDPLUGINBASE_H
class PlayerPluginInterface {
public:
+ using TrackIndex = int;
using TimeStamp = double;
using Volume = double;
virtual void playbackVolumeChanged(Volume) = 0;
virtual void playbackMaxVolumeChanged(Volume) = 0;
- virtual void streamsChanged() = 0;
+ struct Track {
+ QString title;
+ QString language;
+ int id;
+ };
+ using TrackList = QList<Track>;
+ virtual void backendVideoTracksChanged(const TrackList &) = 0;
+ virtual void backendAudioTracksChanged(const TrackList &) = 0;
+ virtual void backendSubtitleTracksChanged(const TrackList &) = 0;
+
+ virtual void backendCurrentVideoTrackChanged(TrackIndex) = 0;
+ virtual void backendCurrentAudioTrackChanged(TrackIndex) = 0;
+ virtual void backendCurrentSubtitleTrackChanged(TrackIndex) = 0;
struct Chapter {
QString title;
--- /dev/null
+#ifndef PLAYERPLUGININTERFACE_H
+#define PLAYERPLUGININTERFACE_H
+
+#include <QtGlobal>
+#include <QUrl>
+
+class QOpenGLFramebufferObject;
+
+class VideoUpdateInterface {
+public:
+ virtual ~VideoUpdateInterface() = default;
+
+ virtual void videoUpdated() = 0;
+};
+
+class PlayerPluginInterface {
+public:
+ using TimeStamp = double;
+ using Volume = double;
+
+ enum class PlayState { Stopped, Paused, Playing };
+ /*
+ * .-----.
+ * | | Error
+ * v |
+ * Stopped -'<--+<-------.
+ * | ^ |
+ * | Load | Error |
+ * v | |
+ * Paused<------+ | File End
+ * | ^ |
+ * | Play | Pause |
+ * v | |
+ * Playing------+--------'
+ */
+
+ virtual ~PlayerPluginInterface() = default;
+
+ virtual void backendReadyToPlay() = 0;
+
+ virtual void backendSourceChanged(QUrl source) = 0;
+ virtual void playStateChanged(PlayState) = 0;
+ virtual void playbackDurationChanged(TimeStamp) = 0;
+ virtual void playbackPositionChanged(TimeStamp) = 0;
+ virtual void playbackVolumeChanged(Volume) = 0;
+ virtual void playbackMaxVolumeChanged(Volume) = 0;
+
+ struct Track {
+ QString title;
+ QString language;
+ int id;
+ };
+ using TrackList = QList<Track>;
+ virtual void backendVideoTracksChanged(const TrackList &) = 0;
+ virtual void backendAudioTracksChanged(const TrackList &) = 0;
+ virtual void backendSubtitleTracksChanged(const TrackList &) = 0;
+
+ struct Chapter {
+ QString title;
+ TimeStamp startTime;
+ };
+ using ChapterList = QList<Chapter>;
+ virtual void backendChaptersChanged(const ChapterList &chapters) = 0;
+};
+
+class PlayerRendererInterface {
+public:
+ virtual ~PlayerRendererInterface() = default;
+ virtual void rendererSinkSet(VideoUpdateInterface *) = 0;
+ virtual void rendererReady() = 0;
+};
+
+class VideoRendererBase {
+public:
+ VideoRendererBase() = default;
+ VideoRendererBase(const VideoRendererBase &) = delete;
+ VideoRendererBase &operator=(const VideoRendererBase &) = delete;
+ virtual ~VideoRendererBase() = default;
+ virtual void render(QOpenGLFramebufferObject *) = 0;
+};
+
+#endif // PLAYERPLUGININTERFACE_H
--- /dev/null
+#ifndef PLAYERPLUGININTERFACE_H
+#define PLAYERPLUGININTERFACE_H
+
+#include <QtGlobal>
+#include <QUrl>
+
+class QOpenGLFramebufferObject;
+
+class VideoUpdateInterface {
+public:
+ virtual ~VideoUpdateInterface() = default;
+
+ virtual void videoUpdated() = 0;
+};
+
+class PlayerPluginInterface {
+public:
+ using TrackIndex = int;
+ using TimeStamp = double;
+ using Volume = double;
+
+ enum class PlayState { Stopped, Paused, Playing };
+ /*
+ * .-----.
+ * | | Error
+ * v |
+ * Stopped -'<--+<-------.
+ * | ^ |
+ * | Load | Error |
+ * v | |
+ * Paused<------+ | File End
+ * | ^ |
+ * | Play | Pause |
+ * v | |
+ * Playing------+--------'
+ */
+
+ virtual ~PlayerPluginInterface() = default;
+
+ virtual void backendReadyToPlay() = 0;
+
+ virtual void backendSourceChanged(QUrl source) = 0;
+ virtual void playStateChanged(PlayState) = 0;
+ virtual void playbackDurationChanged(TimeStamp) = 0;
+ virtual void playbackPositionChanged(TimeStamp) = 0;
+ virtual void playbackVolumeChanged(Volume) = 0;
+ virtual void playbackMaxVolumeChanged(Volume) = 0;
+
+ struct Track {
+ QString title;
+ QString language;
+ int id;
+ };
+ using TrackList = QList<Track>;
+ virtual void backendVideoTracksChanged(const TrackList &) = 0;
+ virtual void backendAudioTracksChanged(const TrackList &) = 0;
+ virtual void backendSubtitleTracksChanged(const TrackList &) = 0;
+
+ virtual void backendCurrentVideoTrackChanged(TrackIndex) = 0;
+ virtual void backendCurrentAudioTrackChanged(TrackIndex) = 0;
+ virtual void backendCurrentSubtitleTrackChanged(TrackIndex) = 0;
+
+ struct Chapter {
+ QString title;
+ TimeStamp startTime;
+ };
+ using ChapterList = QList<Chapter>;
+ virtual void backendChaptersChanged(const ChapterList &chapters) = 0;
+};
+
+class PlayerRendererInterface {
+public:
+ virtual ~PlayerRendererInterface() = default;
+ virtual void rendererSinkSet(VideoUpdateInterface *) = 0;
+ virtual void rendererReady() = 0;
+};
+
+class VideoRendererBase {
+public:
+ VideoRendererBase() = default;
+ VideoRendererBase(const VideoRendererBase &) = delete;
+ VideoRendererBase &operator=(const VideoRendererBase &) = delete;
+ virtual ~VideoRendererBase() = default;
+ virtual void render(QOpenGLFramebufferObject *) = 0;
+};
+
+#endif // PLAYERPLUGININTERFACE_H
--- /dev/null
+#ifndef PLAYERPLUGININTERFACE_H
+#define PLAYERPLUGININTERFACE_H
+
+#include <QtGlobal>
+#include <QUrl>
+
+class QOpenGLFramebufferObject;
+
+class VideoUpdateInterface {
+public:
+ virtual ~VideoUpdateInterface() = default;
+
+ virtual void videoUpdated() = 0;
+};
+
+class PlayerPluginInterface {
+public:
+ using TimeStamp = double;
+ using Volume = double;
+
+ enum class PlayState { Stopped, Paused, Playing };
+ /*
+ * .-----.
+ * | | Error
+ * v |
+ * Stopped -'<--+<-------.
+ * | ^ |
+ * | Load | Error |
+ * v | |
+ * Paused<------+ | File End
+ * | ^ |
+ * | Play | Pause |
+ * v | |
+ * Playing------+--------'
+ */
+
+ virtual ~PlayerPluginInterface() = default;
+
+ virtual void backendReadyToPlay() = 0;
+
+ virtual void backendSourceChanged(QUrl source) = 0;
+ virtual void playStateChanged(PlayState) = 0;
+ virtual void playbackDurationChanged(TimeStamp) = 0;
+ virtual void playbackPositionChanged(TimeStamp) = 0;
+ virtual void playbackVolumeChanged(Volume) = 0;
+ virtual void playbackMaxVolumeChanged(Volume) = 0;
+
+ struct Track {
+ QString title;
+ QString language;
+ };
+ using TrackList = QList<Track>;
+ virtual void backendTracksChanged(const TrackList &tracks) = 0;
+
+ struct Chapter {
+ QString title;
+ TimeStamp startTime;
+ };
+ using ChapterList = QList<Chapter>;
+ virtual void backendChaptersChanged(const ChapterList &chapters) = 0;
+};
+
+class PlayerRendererInterface {
+public:
+ virtual ~PlayerRendererInterface() = default;
+ virtual void rendererSinkSet(VideoUpdateInterface *) = 0;
+ virtual void rendererReady() = 0;
+};
+
+class VideoRendererBase {
+public:
+ VideoRendererBase() = default;
+ VideoRendererBase(const VideoRendererBase &) = delete;
+ VideoRendererBase &operator=(const VideoRendererBase &) = delete;
+ virtual ~VideoRendererBase() = default;
+ virtual void render(QOpenGLFramebufferObject *) = 0;
+};
+
+#endif // PLAYERPLUGININTERFACE_H
--- /dev/null
+#ifndef PLAYERPLUGININTERFACE_H
+#define PLAYERPLUGININTERFACE_H
+
+#include <QtGlobal>
+#include <QUrl>
+
+class QOpenGLFramebufferObject;
+
+class VideoUpdateInterface {
+public:
+ virtual ~VideoUpdateInterface() = default;
+
+ virtual void videoUpdated() = 0;
+};
+
+class PlayerPluginInterface {
+public:
+ using TrackIndex = int;
+ using TimeStamp = double;
+ using Volume = double;
+
+ enum class PlayState { Stopped, Paused, Playing };
+ /*
+ * .-----.
+ * | | Error
+ * v |
+ * Stopped -'<--+<-------.
+ * | ^ |
+ * | Load | Error |
+ * v | |
+ * Paused<------+ | File End
+ * | ^ |
+ * | Play | Pause |
+ * v | |
+ * Playing------+--------'
+ */
+
+ virtual ~PlayerPluginInterface() = default;
+
+ virtual void backendReadyToPlay() = 0;
+
+ virtual void backendSourceChanged(QUrl source) = 0;
+ virtual void playStateChanged(PlayState) = 0;
+ virtual void playbackDurationChanged(TimeStamp) = 0;
+ virtual void playbackPositionChanged(TimeStamp) = 0;
+ virtual void playbackVolumeChanged(Volume) = 0;
+ virtual void playbackMaxVolumeChanged(Volume) = 0;
+
+ struct Track {
+ QString title;
+ QString language;
+ int id;
+ };
+ using TrackList = QList<Track>;
+ virtual void backendVideoTracksChanged(const TrackList &) = 0;
+ virtual void backendAudioTracksChanged(const TrackList &) = 0;
+ virtual void backendSubtitleTracksChanged(const TrackList &) = 0;
+
+ virtual void backendCurrentVideoTrackChanged(TrackIndex) = 0;
+ virtual void backendCurrentAudioTrackChanged(TrackIndex) = 0;
+ virtual void backendCurrentSubtitleTrackChanged(TrackIndex) = 0;
+
+ struct Chapter {
+ QString title;
+ TimeStamp startTime;
+ };
+ using ChapterList = QList<Chapter>;
+ virtual void backendChaptersChanged(const ChapterList &chapters) = 0;
+};
+
+class PlayerRendererInterface {
+public:
+ virtual ~PlayerRendererInterface() = default;
+ virtual void rendererSinkSet(VideoUpdateInterface *) = 0;
+ virtual void rendererReady() = 0;
+};
+
+class VideoRendererBase {
+public:
+ VideoRendererBase() = default;
+ VideoRendererBase(const VideoRendererBase &) = delete;
+ VideoRendererBase &operator=(const VideoRendererBase &) = delete;
+ virtual ~VideoRendererBase() = default;
+ virtual void render(QOpenGLFramebufferObject *) = 0;
+};
+
+#endif // PLAYERPLUGININTERFACE_H
m_backend = backendPlugin->createInstance(this);
Q_CHECK_PTR(m_backend);
+ m_videoTrackModel = new TrackModel{this};
+ m_videoTrackModel->type = "video";
+ Q_CHECK_PTR(m_videoTrackModel);
+ connect(m_videoTrackModel, SIGNAL(trackChangeRequested(int)), this,
+ SLOT(setCurrentVideoTrack(int)));
+ m_audioTrackModel = new TrackModel{this};
+ m_audioTrackModel->type = "audio";
+ Q_CHECK_PTR(m_audioTrackModel);
+ connect(m_audioTrackModel, SIGNAL(trackChangeRequested(int)), this,
+ SLOT(setCurrentAudioTrack(int)));
+ m_subtitleTrackModel = new TrackModel{this};
+ m_subtitleTrackModel->type = "sub";
+ Q_CHECK_PTR(m_subtitleTrackModel);
+ connect(m_subtitleTrackModel, SIGNAL(trackChangeRequested(int)), this,
+ SLOT(setCurrentSubtitleTrack(int)));
+
m_chapterModel = new ChapterModel{this};
Q_CHECK_PTR(m_chapterModel);
}
return m_availableAudioStreams;
}
-Player::StreamIndex Player::currentAudioStream() const {
- return m_currentAudioStream;
+Player::TrackIndex Player::currentAudioTrack() const {
+ return m_currentAudioTrack;
}
-Player::StreamIndex Player::currentVideoStream() const {
- return m_currentVideoStream;
+Player::TrackIndex Player::currentVideoTrack() const {
+ return m_currentVideoTrack;
}
-Player::StreamIndex Player::currentSubtitleStream() const {
- return m_currentSubtitleStream;
+Player::TrackIndex Player::currentSubtitleTrack() const {
+ return m_currentSubtitleTrack;
}
Player::VideoStreams Player::availableVideoStreams() const {
double Player::position() const { return m_position; }
+QAbstractItemModel *Player::videoTrackModel() const {
+ return m_videoTrackModel;
+}
+
+QAbstractItemModel *Player::audioTrackModel() const {
+ return m_audioTrackModel;
+}
+
+QAbstractItemModel *Player::subtitleTrackModel() const {
+ return m_subtitleTrackModel;
+}
+
QAbstractItemModel *Player::chapterModel() const { return m_chapterModel; }
void Player::load(const QUrl &resource) {
void Player::toggleMuted() { setMuted(!muted()); }
-void Player::setCurrentAudioStream(Player::StreamIndex currentAudioStream) {
- if (m_currentAudioStream == currentAudioStream)
+void Player::setCurrentVideoTrack(Player::TrackIndex currentVideoStream) {
+ if (m_currentVideoTrack == currentVideoStream)
return;
- m_currentAudioStream = currentAudioStream;
- emit currentAudioStreamChanged(currentAudioStream);
+ m_backend->setCurrentVideoStream(currentVideoStream);
}
-void Player::setCurrentVideoStream(Player::StreamIndex currentVideoStream) {
- if (m_currentVideoStream == currentVideoStream)
+void Player::setCurrentAudioTrack(Player::TrackIndex currentAudioStream) {
+ if (m_currentAudioTrack == currentAudioStream)
return;
- m_currentVideoStream = currentVideoStream;
- emit currentVideoStreamChanged(currentVideoStream);
+ m_backend->setCurrentAudioStream(currentAudioStream);
}
-void Player::setCurrentSubtitleStream(
- Player::StreamIndex currentSubtitleStream) {
- if (m_currentSubtitleStream == currentSubtitleStream)
+void Player::setCurrentSubtitleTrack(Player::TrackIndex currentSubtitleStream) {
+ if (m_currentSubtitleTrack == currentSubtitleStream)
return;
- m_currentSubtitleStream = currentSubtitleStream;
- emit currentSubtitleStreamChanged(currentSubtitleStream);
+ m_backend->setCurrentSubtitleStream(currentSubtitleStream);
}
void Player::backendReadyToPlay() {
emit maxVolumeChanged(volume);
}
-void Player::streamsChanged() {}
+void Player::backendVideoTracksChanged(
+ const PlayerPluginInterface::TrackList &tracks) {
+ m_videoTrackModel->setTracks(tracks);
+}
+
+void Player::backendAudioTracksChanged(
+ const PlayerPluginInterface::TrackList &tracks) {
+ m_audioTrackModel->setTracks(tracks);
+}
+
+void Player::backendSubtitleTracksChanged(
+ const PlayerPluginInterface::TrackList &tracks) {
+ m_subtitleTrackModel->setTracks(tracks);
+}
void Player::backendChaptersChanged(
const PlayerPluginInterface::ChapterList &chapters) {
m_chapterModel->setChapters(chapters);
}
+void Player::backendCurrentVideoTrackChanged(Player::TrackIndex track) {
+ if (m_currentVideoTrack == track)
+ return;
+ m_currentVideoTrack = track;
+ emit currentVideoTrackChanged(track);
+}
+
+void Player::backendCurrentAudioTrackChanged(Player::TrackIndex track) {
+ if (m_currentAudioTrack == track)
+ return;
+ m_currentAudioTrack = track;
+ emit currentAudioTrackChanged(track);
+}
+
+void Player::backendCurrentSubtitleTrackChanged(Player::TrackIndex track) {
+ if (m_currentSubtitleTrack == track)
+ return;
+ m_currentSubtitleTrack = track;
+ emit currentSubtitleTrackChanged(track);
+}
+
void Player::reqisterQmlTypes() {
qRegisterMetaType<TimeStamp>("TimeStamp");
- qRegisterMetaType<StreamIndex>("StreamIndex");
+ qRegisterMetaType<TrackIndex>("StreamIndex");
qRegisterMetaType<Volume>("Volume");
qmlRegisterType<Player>("org.aptx.aniplayer", 1, 0, "Player");
}
#include "aniplayer/backendpluginbase.h"
#include "chaptermodel.h"
+#include "trackmodel.h"
class Player : public QObject,
public PlayerPluginInterface,
Q_PROPERTY(double maxVolume READ maxVolume NOTIFY maxVolumeChanged)
Q_PROPERTY(bool muted READ muted WRITE setMuted NOTIFY mutedChanged)
- Q_PROPERTY(Player::StreamIndex currentAudioStream READ currentAudioStream
- WRITE setCurrentAudioStream NOTIFY currentAudioStreamChanged)
- Q_PROPERTY(Player::StreamIndex currentVideoStream READ currentVideoStream
- WRITE setCurrentVideoStream NOTIFY currentVideoStreamChanged)
- Q_PROPERTY(
- Player::StreamIndex currentSubtitleStream READ currentSubtitleStream WRITE
- setCurrentSubtitleStream NOTIFY currentSubtitleStreamChanged)
+ Q_PROPERTY(int currentVideoTrack READ currentVideoTrack WRITE
+ setCurrentVideoTrack NOTIFY currentVideoTrackChanged)
+ Q_PROPERTY(int currentAudioTrack READ currentAudioTrack WRITE
+ setCurrentAudioTrack NOTIFY currentAudioTrackChanged)
+ Q_PROPERTY(int currentSubtitleTrack READ currentSubtitleTrack WRITE
+ setCurrentSubtitleTrack NOTIFY currentSubtitleTrackChanged)
Q_PROPERTY(Player::AudioStreams availableAudioStreams READ
availableAudioStreams NOTIFY availableAudioStreamsChanged)
Q_PROPERTY(
Player::SubtitleStreams availableSubtitleStreams READ
availableSubtitleStreams NOTIFY availableSubtitleStreamsChanged)
+ Q_PROPERTY(QAbstractItemModel *videoTrackModel READ videoTrackModel NOTIFY
+ videoTrackModelChanged)
+ Q_PROPERTY(QAbstractItemModel *audioTrackModel READ audioTrackModel NOTIFY
+ audioTrackModelChanged)
+ Q_PROPERTY(QAbstractItemModel *subtitleTrackModel READ subtitleTrackModel
+ NOTIFY subtitleTrackModelChanged)
Q_PROPERTY(QAbstractItemModel *chapterModel READ chapterModel NOTIFY
chapterModelChanged)
public:
- using StreamIndex = int;
+ using TrackIndex = int;
using AudioStreams = QList<QString>;
using VideoStreams = QList<QString>;
using SubtitleStreams = QList<QString>;
Volume maxVolume() const;
bool muted() const;
- StreamIndex currentAudioStream() const;
- StreamIndex currentVideoStream() const;
- StreamIndex currentSubtitleStream() const;
+ TrackIndex currentAudioTrack() const;
+ TrackIndex currentVideoTrack() const;
+ TrackIndex currentSubtitleTrack() const;
AudioStreams availableAudioStreams() const;
VideoStreams availableVideoStreams() const;
double duration() const;
double position() const;
+ QAbstractItemModel *videoTrackModel() const;
+ QAbstractItemModel *audioTrackModel() const;
+ QAbstractItemModel *subtitleTrackModel() const;
QAbstractItemModel *chapterModel() const;
signals:
void
availableSubtitleStreamsChanged(SubtitleStreams availableSubtitleStreams);
- void currentAudioStreamChanged(int currentAudioStream);
- void currentVideoStreamChanged(StreamIndex currentVideoStream);
- void currentSubtitleStreamChanged(StreamIndex currentSubtitleStream);
+ void currentVideoTrackChanged(int currentVideoTrack);
+ void currentAudioTrackChanged(int currentAudioTrack);
+ void currentSubtitleTrackChanged(int currentSubtitleTrack);
void currentSourceChanged(QUrl currentSource);
void nextSourceChanged(QUrl nextSource);
void durationChanged(double duration);
void positionChanged(double position);
+ void videoTrackModelChanged(QAbstractItemModel *videoTrackModel);
+ void audioTrackModelChanged(QAbstractItemModel *audioTrackModel);
+ void subtitleTrackModelChanged(QAbstractItemModel *subtitleTrackModel);
void chapterModelChanged(QAbstractItemModel *chapterModel);
public slots:
void toggleMuted();
// Streams
- void setCurrentAudioStream(StreamIndex currentAudioStream);
- void setCurrentVideoStream(StreamIndex currentVideoStream);
- void setCurrentSubtitleStream(StreamIndex currentSubtitleStream);
+ void setCurrentAudioTrack(int currentAudioTrack);
+ void setCurrentVideoTrack(int currentVideoTrack);
+ void setCurrentSubtitleTrack(int currentSubtitleTrack);
protected:
void backendReadyToPlay() override;
void playbackPositionChanged(TimeStamp) override;
void playbackVolumeChanged(Volume) override;
void playbackMaxVolumeChanged(Volume) override;
- void streamsChanged() override;
+ void backendVideoTracksChanged(const TrackList &) override;
+ void backendAudioTracksChanged(const TrackList &) override;
+ void backendSubtitleTracksChanged(const TrackList &) override;
void backendChaptersChanged(const ChapterList &chapters) override;
+ void backendCurrentVideoTrackChanged(TrackIndex) override;
+ void backendCurrentAudioTrackChanged(TrackIndex) override;
+ void backendCurrentSubtitleTrackChanged(TrackIndex) override;
+
public:
static void reqisterQmlTypes();
Volume m_volume = MAX_VOLUME;
Volume m_maxVolume = MAX_VOLUME;
AudioStreams m_availableAudioStreams;
- StreamIndex m_currentAudioStream = StreamIndex{};
- StreamIndex m_currentVideoStream = StreamIndex{};
- StreamIndex m_currentSubtitleStream = StreamIndex{};
+ TrackIndex m_currentAudioTrack = TrackIndex{};
+ TrackIndex m_currentVideoTrack = TrackIndex{};
+ TrackIndex m_currentSubtitleTrack = TrackIndex{};
VideoStreams m_availableVideoStreams;
SubtitleStreams m_availableSubtitleStreams;
Player::TimeStamp m_duration = 0;
Player::TimeStamp m_position = 0;
VideoUpdateInterface *m_renderer = nullptr;
+ TrackModel *m_videoTrackModel;
+ TrackModel *m_audioTrackModel;
+ TrackModel *m_subtitleTrackModel;
ChapterModel *m_chapterModel;
bool m_muted = false;
bool m_backendInstanceReady = false;
};
Q_DECLARE_METATYPE(Player::PlayState)
-Q_DECLARE_METATYPE(Player::StreamIndex)
+Q_DECLARE_METATYPE(Player::TrackIndex)
Q_DECLARE_METATYPE(Player::TimeStamp)
// Q_DECLARE_METATYPE(Player::Volume)
--- /dev/null
+#include "trackmodel.h"
+
+TrackModel::TrackModel(QObject *parent) : QAbstractListModel{parent} {}
+
+void TrackModel::setTracks(const PlayerPluginInterface::TrackList &data) {
+ beginResetModel();
+ m_data = data;
+ endResetModel();
+}
+
+QHash<int, QByteArray> TrackModel::roleNames() const {
+ static QHash<int, QByteArray> roles{
+ {Qt::DisplayRole, "text"},
+ {TitleRole, "title"},
+ {LanguageRole, "language"},
+ {IdRole, "id"},
+ };
+ return roles;
+}
+
+int TrackModel::rowCount(const QModelIndex &parent) const {
+ if (parent.isValid())
+ return 0;
+ return m_data.size() + 1;
+}
+
+QVariant TrackModel::data(const QModelIndex &index, int role) const {
+ static PlayerPluginInterface::Track noneTrackData{"None", "", -1};
+
+ auto &trackData =
+ (index.row() == 0) ? noneTrackData : m_data[index.row() - 1];
+
+ switch (role) {
+ case Qt::DisplayRole:
+ return QString{"[%4] %1: %2 (%3)"}
+ .arg(trackData.id)
+ .arg(trackData.title)
+ .arg(trackData.language)
+ .arg(type);
+ case TitleRole:
+ return trackData.title;
+ case LanguageRole:
+ return trackData.language;
+ case IdRole:
+ return trackData.id;
+ }
+ return {};
+}
+
+void TrackModel::requestTrackChangeForRow(int row)
+{
+ if (row < 0 || row >= rowCount())
+ return;
+ emit trackChangeRequested(data(index(row, 0), IdRole).toInt());
+}
+
+int TrackModel::rowForTrackIndex(int trackIndex)
+{
+ if (trackIndex == -1)
+ return 0;
+ for (int i = 0; i < m_data.size(); ++i)
+ if (m_data[i].id == trackIndex)
+ return i + 1;
+ return 0;
+}
--- /dev/null
+#ifndef TRACKMODEL_H
+#define TRACKMODEL_H
+
+#include <QAbstractListModel>
+
+#include "aniplayer/playerplugininterface.h"
+
+class TrackModel : public QAbstractListModel {
+ Q_OBJECT
+public:
+ enum TrackRoles { TitleRole = Qt::UserRole + 1, LanguageRole, IdRole };
+
+ explicit TrackModel(QObject *parent = nullptr);
+
+ void setTracks(const PlayerPluginInterface::TrackList &);
+
+ QHash<int, QByteArray> roleNames() const override;
+ int rowCount(const QModelIndex &parent = QModelIndex{}) const override;
+ QVariant data(const QModelIndex &index, int role) const override;
+
+ QString type;
+
+public slots:
+ void requestTrackChangeForRow(int row);
+ int rowForTrackIndex(int trackIndex);
+
+signals:
+ void trackChangeRequested(int track);
+
+private:
+ PlayerPluginInterface::TrackList m_data;
+};
+
+#endif // TRACKMODEL_H
volume: controlledPlayer ? controlledPlayer.volume : 1
onVolumeChangeRequested: controlledPlayer.setVolume(volume)
}
+ ComboBox {
+ id: videoTracks
+ model: player.videoTrackModel
+ textRole: "text"
+ currentIndex: player.videoTrackModel.rowForTrackIndex(player.currentVideoTrack)
+ onActivated: player.videoTrackModel.requestTrackChangeForRow(index)
+ }
+ ComboBox {
+ id: audioTracks
+ model: player.audioTrackModel
+ textRole: "text"
+ currentIndex: player.audioTrackModel.rowForTrackIndex(player.currentAudioTrack)
+ onActivated: player.audioTrackModel.requestTrackChangeForRow(index)
+ }
+ ComboBox {
+ id: subtitleTracks
+ model: player.subtitleTrackModel
+ textRole: "text"
+ currentIndex: player.subtitleTrackModel.rowForTrackIndex(player.currentSubtitleTrack)
+ onActivated: player.subtitleTrackModel.requestTrackChangeForRow(index)
+ }
}