playback/player: qt: add stream selection support

This commit is contained in:
Alexandre Moreno 2015-10-22 23:05:35 +08:00 committed by Sebastian Dröge
parent 2ea1c9aee7
commit c8d7277d70
5 changed files with 739 additions and 129 deletions

View file

@ -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 {
}
}
}
}
}
}

View file

@ -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);

View file

@ -27,7 +27,7 @@
class QuickRenderer;
class Player : public QGstPlayer
class Player : public QGstPlayer::Player
{
Q_OBJECT
public:

View file

@ -24,43 +24,200 @@
#include <QMetaObject>
#include <functional>
#include <QThread>
#include <QtAlgorithms>
class QGstPlayerRegisterMetaTypes
namespace QGstPlayer {
class RegisterMetaTypes
{
public:
QGstPlayerRegisterMetaTypes()
RegisterMetaTypes()
{
qRegisterMetaType<QGstPlayer::State>("State");
qRegisterMetaType<Player::State>("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<QObject *> &MediaInfo::videoStreams() const
{
return videoStreams_;
}
const QList<QObject *> &MediaInfo::audioStreams() const
{
return audioStreams_;
}
const QList<QObject*> &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<GstPlayerSubtitleInfo*>(data);
QList<QObject*> *subs = static_cast<QList<QObject*>*>(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<GstPlayerVideoInfo*>(data);
QList<QObject*> *videos = static_cast<QList<QObject*>*>(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<GstPlayerAudioInfo*>(data);
QList<QObject*> *audios = static_cast<QList<QObject*>*>(user_data);
audios->append(new AudioInfo(info));
}, &audioStreams_);
}
bool QGstPlayer::MediaInfo::isSeekable() const
VideoInfo::VideoInfo(GstPlayerVideoInfo *info)
: StreamInfo(reinterpret_cast<GstPlayerStreamInfo*>(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<GstPlayerStreamInfo*>(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<GstPlayerStreamInfo*>(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<VideoInfo*>();
Q_ASSERT(info);
gst_player_set_video_track(player_, info->index());
}
void Player::setCurrentAudio(QVariant track)
{
Q_ASSERT(player_ != 0);
AudioInfo* info = track.value<AudioInfo*>();
Q_ASSERT(info);
gst_player_set_audio_track(player_, info->index());
}
void Player::setCurrentSubtitle(QVariant track)
{
Q_ASSERT(player_ != 0);
SubtitleInfo* info = track.value<SubtitleInfo*>();
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<QGstPlayer::State>(state);
player->state_ = static_cast<Player::State>(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<GstPlayerVideoRenderer*>
(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<GParamFlags>(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_properties (gobject_class,

View file

@ -28,7 +28,16 @@
#include <QtQml/QQmlPropertyMap>
#include <gst/player/player.h>
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<QObject*> videoStreams READ videoStreams CONSTANT)
Q_PROPERTY(QList<QObject*> audioStreams READ audioStreams CONSTANT)
Q_PROPERTY(QList<QObject*> subtitleStreams READ subtitleStreams CONSTANT)
public:
explicit MediaInfo(Player *player = 0);
QString uri() const;
QString title() const;
bool isSeekable() const;
const QList<QObject*> &videoStreams() const;
const QList<QObject*> &audioStreams() const;
const QList<QObject*> &subtitleStreams() const;
signals:
void uriChanged();
void seekableChanged();
void titleChanged();
public Q_SLOTS:
void update(GstPlayerMediaInfo *info);
private:
QString uri_;
QString title_;
bool isSeekable_;
QList<QObject*> videoStreams_;
QList<QObject*> audioStreams_;
QList<QObject*> 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" {