From c8d7277d70378affb9096544f3b3edaa87fcf2c3 Mon Sep 17 00:00:00 2001 From: Alexandre Moreno Date: Thu, 22 Oct 2015 23:05:35 +0800 Subject: [PATCH] playback/player: qt: add stream selection support --- playback/player/qt/main.qml | 294 +++++++++++++++++++++-- playback/player/qt/player.cpp | 2 +- playback/player/qt/player.h | 2 +- playback/player/qt/qgstplayer.cpp | 377 ++++++++++++++++++++++++------ playback/player/qt/qgstplayer.h | 193 +++++++++++---- 5 files changed, 739 insertions(+), 129 deletions(-) diff --git a/playback/player/qt/main.qml b/playback/player/qt/main.qml index c7fe42435e..9825026f2e 100644 --- a/playback/player/qt/main.qml +++ b/playback/player/qt/main.qml @@ -44,7 +44,7 @@ ApplicationWindow { volume: 0.5 onStateChanged: { if (state === Player.STOPPED) { - playbutton.text = FontAwesome.Icon.Play + playbutton.state = "play" } } onResolutionChanged: { @@ -116,16 +116,236 @@ ApplicationWindow { interval: 10000 onTriggered: { parent.opacity = 0.0 + settings.visible = false stop() } } + Rectangle { + id: settings + width: 150; height: settingsView.contentHeight + color: Qt.rgba(1, 1, 1, 0.7) + anchors.right: parent.right + anchors.bottom: parent.top + anchors.bottomMargin: 3 + border.width: 1 + border.color: "white" + radius: 5 + visible: false + + ListModel { + id: settingsModel + ListElement { + name: "Video" + } + ListElement { + name: "Audio" + } + ListElement { + name: "Subtitle" + } + } + + Component { + id: settingsDelegate + Item { + width: 150; height: 20 + Text { + text: model.name + font.pointSize: 13 + anchors.centerIn: parent + } + + Text { + font.pointSize: 13 + font.family: "FontAwesome" + text: FontAwesome.Icon.ChevronRight + anchors.right: parent.right + anchors.rightMargin: 10 + anchors.verticalCenter: parent.verticalCenter + } + + MouseArea { + anchors.fill: parent + onClicked: { + switch(name) { + case 'Video': + videos.visible = true + break + case 'Audio': + audios.visible = true + break + case 'Subtitle' : + subtitles.visible = true + break + } + settings.visible = false + } + } + } + } + + ListView { + id: settingsView + anchors.fill: parent + model: settingsModel + delegate: settingsDelegate + } + } + + Rectangle { + id: videos + width: 150; height: videoView.contentHeight + color: Qt.rgba(1, 1, 1, 0.7) + anchors.right: parent.right + anchors.bottom: parent.top + anchors.bottomMargin: 3 + border.width: 1 + border.color: "white" + radius: 5 + + property bool selected: ListView.isCurrentItem + visible: false + + Component { + id: videoDelegate + Item { + width: 150; height: 20 + Text { + text: model.modelData.resolution.width + 'x' + model.modelData.resolution.height + font.pointSize: 13 + anchors.centerIn: parent + } + + MouseArea { + anchors.fill: parent + onClicked: { + parent.ListView.view.currentIndex = index + player.currentVideo = model.modelData + } + } + } + } + + ListView { + id : videoView + anchors.fill: parent + model: player.mediaInfo.videoStreams + delegate: videoDelegate + highlight: Rectangle { + color: "white" + radius: 5 + border.width: 1 + border.color: "white" + } + focus: true + clip: true + } + } + + Rectangle { + id: audios + width: 150; height: audioView.contentHeight + color: Qt.rgba(1, 1, 1, 0.7) + anchors.right: parent.right + anchors.bottom: parent.top + anchors.bottomMargin: 3 + border.width: 1 + border.color: "white" + radius: 5 + + property bool selected: ListView.isCurrentItem + visible: false + + Component { + id: audioDelegate + Item { + width: 150; height: 20 + Text { + text: model.modelData.channels + 'channels' + font.pointSize: 13 + anchors.centerIn: parent + } + + MouseArea { + anchors.fill: parent + onClicked: { + parent.ListView.view.currentIndex = index + player.currentAudio = model.modelData + } + } + } + } + + ListView { + id : audioView + anchors.fill: parent + model: player.mediaInfo.audioStreams + delegate: audioDelegate + highlight: Rectangle { + color: "white" + radius: 5 + border.width: 1 + border.color: "white" + } + focus: true + clip: true + } + } + + Rectangle { + id: subtitles + width: 150; height: subtitleView.contentHeight + color: Qt.rgba(1, 1, 1, 0.7) + anchors.right: parent.right + anchors.bottom: parent.top + anchors.bottomMargin: 3 + border.width: 1 + border.color: "white" + radius: 5 + + property bool selected: ListView.isCurrentItem + visible: false + + Component { + id: subtitleDelegate + Item { + width: 150; height: 20 + Text { + text: model.modelData.language + font.pointSize: 13 + anchors.centerIn: parent + } + + MouseArea { + anchors.fill: parent + onClicked: { + parent.ListView.view.currentIndex = index + player.currentSubtitle = model.modelData + } + } + } + } + + ListView { + id : subtitleView + anchors.fill: parent + model: player.mediaInfo.subtitleStreams + delegate: subtitleDelegate + highlight: Rectangle { + color: "white" + radius: 5 + border.width: 1 + border.color: "white" + } + focus: true + clip: true + } + } + Grid { id: grid anchors.horizontalCenter: parent.horizontalCenter -// anchors.top: parent.top -// anchors.topMargin: 5 - spacing: 7 rows: 1 verticalItemAlignment: Qt.AlignVCenter @@ -161,10 +381,27 @@ ApplicationWindow { Text { anchors.centerIn: parent id : playbutton - font.pointSize: 25 font.family: "FontAwesome" - //font.weight: Font.Light - text: FontAwesome.Icon.PlayCircle + state: "play" + + states: [ + State { + name: "play" + PropertyChanges { + target: playbutton + text: FontAwesome.Icon.PlayCircle + font.pointSize: 25 + } + }, + State { + name: "pause" + PropertyChanges { + target: playbutton + text: FontAwesome.Icon.Pause + font.pointSize: 17 + } + } + ] } MouseArea { @@ -173,12 +410,10 @@ ApplicationWindow { onPressed: { if (player.state !== Player.PLAYING) { player.play() - playbutton.text = FontAwesome.Icon.Pause - playbutton.font.pointSize = 17 + playbutton.state = "pause" } else { player.pause() - playbutton.text = FontAwesome.Icon.PlayCircle - playbutton.font.pointSize = 25 + playbutton.state = "play" } } } @@ -221,8 +456,6 @@ ApplicationWindow { } } - - Item { width: 40 height: 17 @@ -238,11 +471,25 @@ ApplicationWindow { } } + Text { + id: sub + font.pointSize: 17 + font.family: "FontAwesome" + text: FontAwesome.Icon.ClosedCaptions + color: player.subtitleEnabled ? "red" : "black" + + MouseArea { + anchors.fill: parent + onClicked: { + player.subtitleEnabled = !player.subtitleEnabled + } + } + } + Item { width: 17 height: 17 - Text { id : volume anchors.centerIn: parent @@ -343,10 +590,22 @@ ApplicationWindow { } Text { - id: sub + id: cog font.pointSize: 17 font.family: "FontAwesome" - text: FontAwesome.Icon.ClosedCaptions + text: FontAwesome.Icon.Cog + + MouseArea { + anchors.fill: parent + onClicked: { + settings.visible = !settings.visible + videos.visible = false + audios.visible = false + subtitles.visible = false + + + } + } } Text { @@ -380,7 +639,7 @@ ApplicationWindow { maximumValue: player.duration value: player.position onPressedChanged: player.seek(value) - enabled: player.mediaInfo.isSeekable + enabled: player.mediaInfo.seekable anchors.bottom: parent.bottom anchors.horizontalCenter: parent.horizontalCenter @@ -455,7 +714,6 @@ ApplicationWindow { } } } - } } } diff --git a/playback/player/qt/player.cpp b/playback/player/qt/player.cpp index ec4b78cb28..3e2cade32f 100644 --- a/playback/player/qt/player.cpp +++ b/playback/player/qt/player.cpp @@ -29,7 +29,7 @@ Player::Player(QObject *parent) } Player::Player(QObject *parent, QuickRenderer *renderer) - : QGstPlayer(parent, renderer) + : QGstPlayer::Player(parent, renderer) , renderer_(renderer) { renderer_->setParent(this); diff --git a/playback/player/qt/player.h b/playback/player/qt/player.h index 88586f4e03..34a00028f9 100644 --- a/playback/player/qt/player.h +++ b/playback/player/qt/player.h @@ -27,7 +27,7 @@ class QuickRenderer; -class Player : public QGstPlayer +class Player : public QGstPlayer::Player { Q_OBJECT public: diff --git a/playback/player/qt/qgstplayer.cpp b/playback/player/qt/qgstplayer.cpp index c2c0ee0c9d..e6fc476a34 100644 --- a/playback/player/qt/qgstplayer.cpp +++ b/playback/player/qt/qgstplayer.cpp @@ -24,43 +24,200 @@ #include #include #include +#include -class QGstPlayerRegisterMetaTypes +namespace QGstPlayer { + +class RegisterMetaTypes { public: - QGstPlayerRegisterMetaTypes() + RegisterMetaTypes() { - qRegisterMetaType("State"); + qRegisterMetaType("State"); } } _register; -QGstPlayer::MediaInfo::MediaInfo(GstPlayerMediaInfo *media_info) - : mediaInfo_(media_info) +MediaInfo::MediaInfo(Player *player) + : QObject(player) + , uri_() + , title_() + , isSeekable_(false) { } -QString QGstPlayer::MediaInfo::title() const +QString MediaInfo::uri() const { - QString title = QString::fromLocal8Bit - (gst_player_media_info_get_title(mediaInfo_)); + return uri_; +} - // if media has no title, return the file name - if (title.isEmpty()) { - QUrl url(gst_player_media_info_get_uri(mediaInfo_)); - title = url.fileName(); +QString MediaInfo::title() const +{ + return title_; +} + +bool MediaInfo::isSeekable() const +{ + return isSeekable_; +} + +const QList &MediaInfo::videoStreams() const +{ + return videoStreams_; +} + +const QList &MediaInfo::audioStreams() const +{ + return audioStreams_; +} + +const QList &MediaInfo::subtitleStreams() const +{ + return subtitleStreams_; +} + +void MediaInfo::update(GstPlayerMediaInfo *info) +{ + Q_ASSERT(info != 0); + + // FIXME since media-info signal gets emitted + // several times, we just update info iff the + // media uri has changed. + if (uri_ == gst_player_media_info_get_uri(info)) { + return; } - return title; + uri_ = QString(gst_player_media_info_get_uri(info)); + + title_ = QString::fromLocal8Bit(gst_player_media_info_get_title(info)); + + // if media has no title, return the file name + if (title_.isEmpty()) { + QUrl url(gst_player_media_info_get_uri(info)); + title_ = url.fileName(); + } + + emit titleChanged(); + + if (isSeekable_ != gst_player_media_info_is_seekable(info)) { + isSeekable_ = !isSeekable_; + emit seekableChanged(); + } + + if (!subtitleStreams_.isEmpty()) { + qDeleteAll(subtitleStreams_); + subtitleStreams_.clear(); + } + + if (!videoStreams_.isEmpty()) { + qDeleteAll(videoStreams_); + videoStreams_.clear(); + } + + if (!audioStreams_.isEmpty()) { + qDeleteAll(audioStreams_); + audioStreams_.clear(); + } + + GList *list = gst_player_get_subtitle_streams(info); + + g_list_foreach (list, [](gpointer data, gpointer user_data) { + GstPlayerSubtitleInfo *info = static_cast(data); + QList *subs = static_cast*>(user_data); + + subs->append(new SubtitleInfo(info)); + }, &subtitleStreams_); + + list = gst_player_get_video_streams(info); + + g_list_foreach (list, [](gpointer data, gpointer user_data) { + GstPlayerVideoInfo *info = static_cast(data); + QList *videos = static_cast*>(user_data); + + videos->append(new VideoInfo(info)); + }, &videoStreams_); + + list = gst_player_get_audio_streams(info); + + g_list_foreach (list, [](gpointer data, gpointer user_data) { + GstPlayerAudioInfo *info = static_cast(data); + QList *audios = static_cast*>(user_data); + + audios->append(new AudioInfo(info)); + }, &audioStreams_); } -bool QGstPlayer::MediaInfo::isSeekable() const +VideoInfo::VideoInfo(GstPlayerVideoInfo *info) + : StreamInfo(reinterpret_cast(info)) + , video_(info) + , resolution_(gst_player_video_info_get_width(info), gst_player_video_info_get_height(info)) { - return gst_player_media_info_is_seekable(mediaInfo_); + } -bool QGstPlayer::isVideoAvailable() const +QSize VideoInfo::resolution() const +{ + return resolution_; +} + +AudioInfo::AudioInfo(GstPlayerAudioInfo *info) + : StreamInfo(reinterpret_cast(info)) + , audio_(info) + , language_(gst_player_audio_info_get_language(info)) + , channels_(gst_player_audio_info_get_channels(info)) + , bitRate_(gst_player_audio_info_get_bitrate(info)) + , sampleRate_(gst_player_audio_info_get_sample_rate(info)) +{ + +} + +const QString &AudioInfo::language() const +{ + return language_; +} + +int AudioInfo::channels() const +{ + return channels_; +} + +int AudioInfo::bitRate() const +{ + return bitRate_; +} + +int AudioInfo::sampleRate() const +{ + return sampleRate_; +} + +SubtitleInfo::SubtitleInfo(GstPlayerSubtitleInfo *info) + : StreamInfo(reinterpret_cast(info)) + , subtitle_(info) + , language_(gst_player_subtitle_info_get_language(info)) +{ + +} + +const QString &SubtitleInfo::language() const +{ + return language_; +} + +int StreamInfo::index() const +{ + return index_; +} + +StreamInfo::StreamInfo(GstPlayerStreamInfo *info) + : stream_(info) + , index_(gst_player_stream_info_get_index(info)) +{ + +} + +bool Player::isVideoAvailable() const { GstPlayerVideoInfo *video_info; @@ -73,74 +230,163 @@ bool QGstPlayer::isVideoAvailable() const return false; } -QQmlPropertyMap *QGstPlayer::mediaInfo() const +MediaInfo *Player::mediaInfo() const { - return mediaInfoMap_; + return mediaInfo_; } -QGstPlayer::QGstPlayer(QObject *parent, QGstPlayer::VideoRenderer *renderer) +QVariant Player::currentVideo() const +{ + Q_ASSERT(player_ != 0); + + GstPlayerVideoInfo *track = gst_player_get_current_video_track(player_); + + if (!track) + return QVariant(); + + return QVariant::fromValue(new VideoInfo(track)); +} + +QVariant Player::currentAudio() const +{ + Q_ASSERT(player_ != 0); + + GstPlayerAudioInfo *track = gst_player_get_current_audio_track(player_); + + if (!track) + return QVariant(); + + return QVariant::fromValue(new AudioInfo(track)); +} + +QVariant Player::currentSubtitle() const +{ + Q_ASSERT(player_ != 0); + + GstPlayerSubtitleInfo *track = gst_player_get_current_subtitle_track(player_); + + if (!track) + return QVariant(); + + return QVariant::fromValue(new SubtitleInfo(track)); +} + +void Player::setCurrentVideo(QVariant track) +{ + Q_ASSERT(player_ != 0); + + VideoInfo* info = track.value(); + Q_ASSERT(info); + + gst_player_set_video_track(player_, info->index()); +} + +void Player::setCurrentAudio(QVariant track) +{ + Q_ASSERT(player_ != 0); + + AudioInfo* info = track.value(); + Q_ASSERT(info); + + gst_player_set_audio_track(player_, info->index()); + +} + +void Player::setCurrentSubtitle(QVariant track) +{ + Q_ASSERT(player_ != 0); + + SubtitleInfo* info = track.value(); + Q_ASSERT(info); + + gst_player_set_subtitle_track(player_, info->index()); +} + +bool Player::isSubtitleEnabled() const +{ + return subtitleEnabled_; +} + +void Player::setSubtitleEnabled(bool enabled) +{ + Q_ASSERT(player_ != 0); + + subtitleEnabled_ = enabled; + + gst_player_set_subtitle_track_enabled(player_, enabled); + + emit subtitleEnabledChanged(enabled); +} + +Player::Player(QObject *parent, VideoRenderer *renderer) : QObject(parent) , player_() , state_(STOPPED) , videoDimensions_(QSize()) - , mediaInfoMap_() + , mediaInfo_() , videoAvailable_(false) + , subtitleEnabled_(false) { player_ = gst_player_new_full(renderer ? renderer->renderer() : 0, gst_player_qt_signal_dispatcher_new(this)); g_object_connect(player_, - "swapped-signal::state-changed", G_CALLBACK (QGstPlayer::onStateChanged), this, - "swapped-signal::position-updated", G_CALLBACK (QGstPlayer::onPositionUpdated), this, - "swapped-signal::duration-changed", G_CALLBACK (QGstPlayer::onDurationChanged), this, - "swapped-signal::buffering", G_CALLBACK (QGstPlayer::onBufferingChanged), this, - "swapped-signal::video-dimensions-changed", G_CALLBACK (QGstPlayer::onVideoDimensionsChanged), this, - "swapped-signal::volume-changed", G_CALLBACK (QGstPlayer::onVolumeChanged), this, - "swapped-signal::mute-changed", G_CALLBACK (QGstPlayer::onMuteChanged), this, - "swapped-signal::media-info-updated", G_CALLBACK (QGstPlayer::onMediaInfoUpdated), this, NULL); + "swapped-signal::state-changed", G_CALLBACK (Player::onStateChanged), this, + "swapped-signal::position-updated", G_CALLBACK (Player::onPositionUpdated), this, + "swapped-signal::duration-changed", G_CALLBACK (Player::onDurationChanged), this, + "swapped-signal::buffering", G_CALLBACK (Player::onBufferingChanged), this, + "swapped-signal::video-dimensions-changed", G_CALLBACK (Player::onVideoDimensionsChanged), this, + "swapped-signal::volume-changed", G_CALLBACK (Player::onVolumeChanged), this, + "swapped-signal::mute-changed", G_CALLBACK (Player::onMuteChanged), this, + "swapped-signal::media-info-updated", G_CALLBACK (Player::onMediaInfoUpdated), this, NULL); - mediaInfoMap_ = new QQmlPropertyMap(this); + mediaInfo_ = new MediaInfo(this); + gst_player_set_subtitle_track_enabled(player_, false); } void -QGstPlayer::onStateChanged(QGstPlayer * player, GstPlayerState state) +Player::onStateChanged(Player * player, GstPlayerState state) { - player->state_ = static_cast(state); + player->state_ = static_cast(state); emit player->stateChanged(player->state_); } void -QGstPlayer::onPositionUpdated(QGstPlayer * player, GstClockTime position) +Player::onPositionUpdated(Player * player, GstClockTime position) { emit player->positionChanged(position); } void -QGstPlayer::onDurationChanged(QGstPlayer * player, GstClockTime duration) +Player::onDurationChanged(Player * player, GstClockTime duration) { emit player->durationChanged(duration); } void -QGstPlayer::onBufferingChanged(QGstPlayer * player, int percent) +Player::onBufferingChanged(Player * player, int percent) { emit player->bufferingChanged(percent); } void -QGstPlayer::onVideoDimensionsChanged(QGstPlayer * player, int w, int h) +Player::onVideoDimensionsChanged(Player * player, int w, int h) { QSize res(w,h); + if (res == player->videoDimensions_) + return; + player->videoDimensions_ = res; + player->setResolution(res); emit player->resolutionChanged(res); } void -QGstPlayer::onVolumeChanged(QGstPlayer *player) +Player::onVolumeChanged(Player *player) { qreal new_val; @@ -150,7 +396,7 @@ QGstPlayer::onVolumeChanged(QGstPlayer *player) } void -QGstPlayer::onMuteChanged(QGstPlayer *player) +Player::onMuteChanged(Player *player) { bool new_val; @@ -160,15 +406,8 @@ QGstPlayer::onMuteChanged(QGstPlayer *player) } void -QGstPlayer::onMediaInfoUpdated(QGstPlayer *player, GstPlayerMediaInfo *media_info) +Player::onMediaInfoUpdated(Player *player, GstPlayerMediaInfo *media_info) { - MediaInfo mediaInfo(media_info); - - player->mediaInfoMap_->insert(QLatin1String("title"), - QVariant(mediaInfo.title())); - player->mediaInfoMap_->insert(QLatin1String("isSeekable"), - QVariant(mediaInfo.isSeekable())); - bool val = player->isVideoAvailable(); if (player->videoAvailable_ != val) { @@ -176,10 +415,12 @@ QGstPlayer::onMediaInfoUpdated(QGstPlayer *player, GstPlayerMediaInfo *media_inf emit player->videoAvailableChanged(val); } + player->mediaInfo()->update(media_info); + emit player->mediaInfoChanged(); } -QUrl QGstPlayer::source() const +QUrl Player::source() const { Q_ASSERT(player_ != 0); QString url = QString::fromLocal8Bit(gst_player_get_uri(player_)); @@ -187,90 +428,90 @@ QUrl QGstPlayer::source() const return QUrl(url); } -qint64 QGstPlayer::duration() const +qint64 Player::duration() const { Q_ASSERT(player_ != 0); return gst_player_get_duration(player_); } -qint64 QGstPlayer::position() const +qint64 Player::position() const { Q_ASSERT(player_ != 0); return gst_player_get_position(player_); } -qreal QGstPlayer::volume() const +qreal Player::volume() const { Q_ASSERT(player_ != 0); return gst_player_get_volume(player_); } -bool QGstPlayer::isMuted() const +bool Player::isMuted() const { Q_ASSERT(player_ != 0); return gst_player_get_mute(player_); } -int QGstPlayer::buffering() const +int Player::buffering() const { return 0; } -QSize QGstPlayer::resolution() const +QSize Player::resolution() const { return videoDimensions_; } -void QGstPlayer::setResolution(QSize size) +void Player::setResolution(QSize size) { videoDimensions_ = size; } -QGstPlayer::State QGstPlayer::state() const +Player::State Player::state() const { return state_; } -GstElement *QGstPlayer::pipeline() const +GstElement *Player::pipeline() const { Q_ASSERT(player_ != 0); return gst_player_get_pipeline(player_); } -void QGstPlayer::play() +void Player::play() { Q_ASSERT(player_ != 0); gst_player_play(player_); } -void QGstPlayer::pause() +void Player::pause() { Q_ASSERT(player_ != 0); gst_player_pause(player_); } -void QGstPlayer::stop() +void Player::stop() { Q_ASSERT(player_ != 0); gst_player_stop(player_); } -void QGstPlayer::seek(qint64 position) +void Player::seek(qint64 position) { Q_ASSERT(player_ != 0); gst_player_seek(player_, position); } -void QGstPlayer::setSource(QUrl const& url) +void Player::setSource(QUrl const& url) { Q_ASSERT(player_ != 0); QByteArray uri = url.toString().toLocal8Bit(); @@ -280,43 +521,45 @@ void QGstPlayer::setSource(QUrl const& url) emit sourceChanged(url); } -void QGstPlayer::setVolume(qreal val) +void Player::setVolume(qreal val) { Q_ASSERT(player_ != 0); gst_player_set_volume(player_, val); } -void QGstPlayer::setMuted(bool val) +void Player::setMuted(bool val) { Q_ASSERT(player_ != 0); gst_player_set_mute(player_, val); } -void QGstPlayer::setPosition(qint64 pos) +void Player::setPosition(qint64 pos) { Q_ASSERT(player_ != 0); gst_player_seek(player_, pos); } -GstPlayerVideoRenderer *QGstPlayer::VideoRenderer::renderer() +GstPlayerVideoRenderer *VideoRenderer::renderer() { return renderer_; } -QGstPlayer::VideoRenderer::VideoRenderer() +VideoRenderer::VideoRenderer() { renderer_ = static_cast (g_object_new (GST_TYPE_PLAYER_QT_VIDEO_RENDERER, "renderer", this, NULL)); } -QGstPlayer::VideoRenderer::~VideoRenderer() +VideoRenderer::~VideoRenderer() { if (renderer_) gst_object_unref(renderer_); } +} + struct _GstPlayerQtVideoRenderer { GObject parent; @@ -527,7 +770,7 @@ static void qt_signal_dispatcher_param_specs [QT_SIGNAL_DISPATCHER_PROP_PLAYER] = - g_param_spec_pointer ("player", "QGstPlayer instance", "", + g_param_spec_pointer ("player", "Player instance", "", static_cast(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_properties (gobject_class, diff --git a/playback/player/qt/qgstplayer.h b/playback/player/qt/qgstplayer.h index c0c39d0d91..1f4dc6f00d 100644 --- a/playback/player/qt/qgstplayer.h +++ b/playback/player/qt/qgstplayer.h @@ -28,7 +28,16 @@ #include #include -class QGstPlayer : public QObject +namespace QGstPlayer { + +class VideoRenderer; +class MediaInfo; +class StreamInfo; +class VideInfo; +class AudioInfo; +class SubtitleInfo; + +class Player : public QObject { Q_OBJECT Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged) @@ -41,14 +50,16 @@ class QGstPlayer : public QObject Q_PROPERTY(State state READ state NOTIFY stateChanged) Q_PROPERTY(QObject *mediaInfo READ mediaInfo NOTIFY mediaInfoChanged) Q_PROPERTY(bool videoAvailable READ isVideoAvailable NOTIFY videoAvailableChanged) + Q_PROPERTY(QVariant currentVideo READ currentVideo WRITE setCurrentVideo) + Q_PROPERTY(QVariant currentAudio READ currentAudio WRITE setCurrentAudio) + Q_PROPERTY(QVariant currentSubtitle READ currentSubtitle WRITE setCurrentSubtitle) + Q_PROPERTY(bool subtitleEnabled READ isSubtitleEnabled WRITE setSubtitleEnabled + NOTIFY subtitleEnabledChanged) Q_ENUMS(State) public: - - class VideoRenderer; - - explicit QGstPlayer(QObject *parent = 0, VideoRenderer *renderer = 0); + explicit Player(QObject *parent = 0, VideoRenderer *renderer = 0); typedef GstPlayerError Error; enum State { @@ -58,29 +69,6 @@ public: PLAYING = GST_PLAYER_STATE_PLAYING }; - class VideoRenderer - { - public: - GstPlayerVideoRenderer *renderer(); - virtual GstElement *createVideoSink() = 0; - protected: - VideoRenderer(); - virtual ~VideoRenderer(); - private: - GstPlayerVideoRenderer *renderer_; - }; - - // TODO add remaining bits - class MediaInfo - { - public: - MediaInfo(GstPlayerMediaInfo *media_info); - QString title() const; - bool isSeekable() const; - private: - GstPlayerMediaInfo *mediaInfo_; - }; - QUrl source() const; qint64 duration() const; qint64 position() const; @@ -92,8 +80,11 @@ public: QSize resolution() const; void setResolution(QSize size); bool isVideoAvailable() const; - QQmlPropertyMap *mediaInfo() const; - + MediaInfo *mediaInfo() const; + QVariant currentVideo() const; + QVariant currentAudio() const; + QVariant currentSubtitle() const; + bool isSubtitleEnabled() const; signals: void stateChanged(State new_state); @@ -107,6 +98,7 @@ signals: void mediaInfoChanged(); void sourceChanged(QUrl new_url); void videoAvailableChanged(bool videoAvailable); + void subtitleEnabledChanged(bool enabled); public slots: void play(); @@ -117,27 +109,144 @@ public slots: void setVolume(qreal val); void setMuted(bool val); void setPosition(qint64 pos); + void setCurrentVideo(QVariant track); + void setCurrentAudio(QVariant track); + void setCurrentSubtitle(QVariant track); + void setSubtitleEnabled(bool enabled); private: - Q_DISABLE_COPY(QGstPlayer) - static void onStateChanged(QGstPlayer *, GstPlayerState state); - static void onPositionUpdated(QGstPlayer *, GstClockTime position); - static void onDurationChanged(QGstPlayer *, GstClockTime duration); - static void onBufferingChanged(QGstPlayer *, int percent); - static void onVideoDimensionsChanged(QGstPlayer *, int w, int h); - static void onVolumeChanged(QGstPlayer *); - static void onMuteChanged(QGstPlayer *); - static void onMediaInfoUpdated(QGstPlayer *, GstPlayerMediaInfo *media_info); + Q_DISABLE_COPY(Player) + static void onStateChanged(Player *, GstPlayerState state); + static void onPositionUpdated(Player *, GstClockTime position); + static void onDurationChanged(Player *, GstClockTime duration); + static void onBufferingChanged(Player *, int percent); + static void onVideoDimensionsChanged(Player *, int w, int h); + static void onVolumeChanged(Player *); + static void onMuteChanged(Player *); + static void onMediaInfoUpdated(Player *, GstPlayerMediaInfo *media_info); GstPlayer *player_; State state_; QSize videoDimensions_; - QQmlPropertyMap *mediaInfoMap_; + MediaInfo *mediaInfo_; bool videoAvailable_; + bool subtitleEnabled_; }; -Q_DECLARE_METATYPE(QGstPlayer*) -Q_DECLARE_METATYPE(QGstPlayer::State) +class VideoRenderer +{ +public: + GstPlayerVideoRenderer *renderer(); + virtual GstElement *createVideoSink() = 0; +protected: + VideoRenderer(); + virtual ~VideoRenderer(); +private: + GstPlayerVideoRenderer *renderer_; +}; + +class MediaInfo : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString uri READ uri NOTIFY uriChanged) + Q_PROPERTY(bool seekable READ isSeekable NOTIFY seekableChanged) + Q_PROPERTY(QString title READ title NOTIFY titleChanged) + Q_PROPERTY(QList videoStreams READ videoStreams CONSTANT) + Q_PROPERTY(QList audioStreams READ audioStreams CONSTANT) + Q_PROPERTY(QList subtitleStreams READ subtitleStreams CONSTANT) + +public: + explicit MediaInfo(Player *player = 0); + QString uri() const; + QString title() const; + bool isSeekable() const; + const QList &videoStreams() const; + const QList &audioStreams() const; + const QList &subtitleStreams() const; + +signals: + void uriChanged(); + void seekableChanged(); + void titleChanged(); + +public Q_SLOTS: + void update(GstPlayerMediaInfo *info); +private: + QString uri_; + QString title_; + bool isSeekable_; + QList videoStreams_; + QList audioStreams_; + QList subtitleStreams_; +}; + +class StreamInfo : public QObject +{ + Q_OBJECT + Q_PROPERTY(int index READ index CONSTANT) +public: + int index() const; + +protected: + StreamInfo(GstPlayerStreamInfo* info); +private: + GstPlayerStreamInfo *stream_; + int index_; +}; + +class VideoInfo : public StreamInfo +{ + Q_OBJECT + Q_PROPERTY(QSize resolution READ resolution CONSTANT) +public: + VideoInfo(GstPlayerVideoInfo *info); + QSize resolution() const; + +private: + GstPlayerVideoInfo *video_; + QSize resolution_; +}; + +class AudioInfo : public StreamInfo +{ + Q_OBJECT + Q_PROPERTY(QString language READ language CONSTANT) + Q_PROPERTY(int channels READ channels CONSTANT) + Q_PROPERTY(int bitRate READ bitRate CONSTANT) + Q_PROPERTY(int sampleRate READ sampleRate CONSTANT) + +public: + AudioInfo(GstPlayerAudioInfo *info); + QString const& language() const; + int channels() const; + int bitRate() const; + int sampleRate() const; + +private: + GstPlayerAudioInfo *audio_; + QString language_; + int channels_; + int bitRate_; + int sampleRate_; +}; + +class SubtitleInfo : public StreamInfo +{ + Q_OBJECT + Q_PROPERTY(QString language READ language CONSTANT) +public: + SubtitleInfo(GstPlayerSubtitleInfo *info); + QString const& language() const; + +private: + GstPlayerSubtitleInfo *subtitle_; + QString language_; +}; + +} + +Q_DECLARE_METATYPE(QGstPlayer::Player*) +Q_DECLARE_METATYPE(QGstPlayer::Player::State) extern "C" {