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 volume: 0.5
onStateChanged: { onStateChanged: {
if (state === Player.STOPPED) { if (state === Player.STOPPED) {
playbutton.text = FontAwesome.Icon.Play playbutton.state = "play"
} }
} }
onResolutionChanged: { onResolutionChanged: {
@ -116,16 +116,236 @@ ApplicationWindow {
interval: 10000 interval: 10000
onTriggered: { onTriggered: {
parent.opacity = 0.0 parent.opacity = 0.0
settings.visible = false
stop() 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 { Grid {
id: grid id: grid
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
// anchors.top: parent.top
// anchors.topMargin: 5
spacing: 7 spacing: 7
rows: 1 rows: 1
verticalItemAlignment: Qt.AlignVCenter verticalItemAlignment: Qt.AlignVCenter
@ -161,10 +381,27 @@ ApplicationWindow {
Text { Text {
anchors.centerIn: parent anchors.centerIn: parent
id : playbutton id : playbutton
font.pointSize: 25
font.family: "FontAwesome" font.family: "FontAwesome"
//font.weight: Font.Light state: "play"
states: [
State {
name: "play"
PropertyChanges {
target: playbutton
text: FontAwesome.Icon.PlayCircle text: FontAwesome.Icon.PlayCircle
font.pointSize: 25
}
},
State {
name: "pause"
PropertyChanges {
target: playbutton
text: FontAwesome.Icon.Pause
font.pointSize: 17
}
}
]
} }
MouseArea { MouseArea {
@ -173,12 +410,10 @@ ApplicationWindow {
onPressed: { onPressed: {
if (player.state !== Player.PLAYING) { if (player.state !== Player.PLAYING) {
player.play() player.play()
playbutton.text = FontAwesome.Icon.Pause playbutton.state = "pause"
playbutton.font.pointSize = 17
} else { } else {
player.pause() player.pause()
playbutton.text = FontAwesome.Icon.PlayCircle playbutton.state = "play"
playbutton.font.pointSize = 25
} }
} }
} }
@ -221,8 +456,6 @@ ApplicationWindow {
} }
} }
Item { Item {
width: 40 width: 40
height: 17 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 { Item {
width: 17 width: 17
height: 17 height: 17
Text { Text {
id : volume id : volume
anchors.centerIn: parent anchors.centerIn: parent
@ -343,10 +590,22 @@ ApplicationWindow {
} }
Text { Text {
id: sub id: cog
font.pointSize: 17 font.pointSize: 17
font.family: "FontAwesome" 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 { Text {
@ -380,7 +639,7 @@ ApplicationWindow {
maximumValue: player.duration maximumValue: player.duration
value: player.position value: player.position
onPressedChanged: player.seek(value) onPressedChanged: player.seek(value)
enabled: player.mediaInfo.isSeekable enabled: player.mediaInfo.seekable
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter 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) Player::Player(QObject *parent, QuickRenderer *renderer)
: QGstPlayer(parent, renderer) : QGstPlayer::Player(parent, renderer)
, renderer_(renderer) , renderer_(renderer)
{ {
renderer_->setParent(this); renderer_->setParent(this);

View file

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

View file

@ -24,43 +24,200 @@
#include <QMetaObject> #include <QMetaObject>
#include <functional> #include <functional>
#include <QThread> #include <QThread>
#include <QtAlgorithms>
class QGstPlayerRegisterMetaTypes namespace QGstPlayer {
class RegisterMetaTypes
{ {
public: public:
QGstPlayerRegisterMetaTypes() RegisterMetaTypes()
{ {
qRegisterMetaType<QGstPlayer::State>("State"); qRegisterMetaType<Player::State>("State");
} }
} _register; } _register;
QGstPlayer::MediaInfo::MediaInfo(GstPlayerMediaInfo *media_info) MediaInfo::MediaInfo(Player *player)
: mediaInfo_(media_info) : QObject(player)
, uri_()
, title_()
, isSeekable_(false)
{ {
} }
QString QGstPlayer::MediaInfo::title() const QString MediaInfo::uri() const
{ {
QString title = QString::fromLocal8Bit return uri_;
(gst_player_media_info_get_title(mediaInfo_)); }
// if media has no title, return the file name QString MediaInfo::title() const
if (title.isEmpty()) { {
QUrl url(gst_player_media_info_get_uri(mediaInfo_)); return title_;
title = url.fileName(); }
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; GstPlayerVideoInfo *video_info;
@ -73,74 +230,163 @@ bool QGstPlayer::isVideoAvailable() const
return false; 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) : QObject(parent)
, player_() , player_()
, state_(STOPPED) , state_(STOPPED)
, videoDimensions_(QSize()) , videoDimensions_(QSize())
, mediaInfoMap_() , mediaInfo_()
, videoAvailable_(false) , videoAvailable_(false)
, subtitleEnabled_(false)
{ {
player_ = gst_player_new_full(renderer ? renderer->renderer() : 0, player_ = gst_player_new_full(renderer ? renderer->renderer() : 0,
gst_player_qt_signal_dispatcher_new(this)); gst_player_qt_signal_dispatcher_new(this));
g_object_connect(player_, g_object_connect(player_,
"swapped-signal::state-changed", G_CALLBACK (QGstPlayer::onStateChanged), this, "swapped-signal::state-changed", G_CALLBACK (Player::onStateChanged), this,
"swapped-signal::position-updated", G_CALLBACK (QGstPlayer::onPositionUpdated), this, "swapped-signal::position-updated", G_CALLBACK (Player::onPositionUpdated), this,
"swapped-signal::duration-changed", G_CALLBACK (QGstPlayer::onDurationChanged), this, "swapped-signal::duration-changed", G_CALLBACK (Player::onDurationChanged), this,
"swapped-signal::buffering", G_CALLBACK (QGstPlayer::onBufferingChanged), this, "swapped-signal::buffering", G_CALLBACK (Player::onBufferingChanged), this,
"swapped-signal::video-dimensions-changed", G_CALLBACK (QGstPlayer::onVideoDimensionsChanged), this, "swapped-signal::video-dimensions-changed", G_CALLBACK (Player::onVideoDimensionsChanged), this,
"swapped-signal::volume-changed", G_CALLBACK (QGstPlayer::onVolumeChanged), this, "swapped-signal::volume-changed", G_CALLBACK (Player::onVolumeChanged), this,
"swapped-signal::mute-changed", G_CALLBACK (QGstPlayer::onMuteChanged), this, "swapped-signal::mute-changed", G_CALLBACK (Player::onMuteChanged), this,
"swapped-signal::media-info-updated", G_CALLBACK (QGstPlayer::onMediaInfoUpdated), this, NULL); "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 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_); emit player->stateChanged(player->state_);
} }
void void
QGstPlayer::onPositionUpdated(QGstPlayer * player, GstClockTime position) Player::onPositionUpdated(Player * player, GstClockTime position)
{ {
emit player->positionChanged(position); emit player->positionChanged(position);
} }
void void
QGstPlayer::onDurationChanged(QGstPlayer * player, GstClockTime duration) Player::onDurationChanged(Player * player, GstClockTime duration)
{ {
emit player->durationChanged(duration); emit player->durationChanged(duration);
} }
void void
QGstPlayer::onBufferingChanged(QGstPlayer * player, int percent) Player::onBufferingChanged(Player * player, int percent)
{ {
emit player->bufferingChanged(percent); emit player->bufferingChanged(percent);
} }
void void
QGstPlayer::onVideoDimensionsChanged(QGstPlayer * player, int w, int h) Player::onVideoDimensionsChanged(Player * player, int w, int h)
{ {
QSize res(w,h); QSize res(w,h);
if (res == player->videoDimensions_)
return;
player->videoDimensions_ = res;
player->setResolution(res); player->setResolution(res);
emit player->resolutionChanged(res); emit player->resolutionChanged(res);
} }
void void
QGstPlayer::onVolumeChanged(QGstPlayer *player) Player::onVolumeChanged(Player *player)
{ {
qreal new_val; qreal new_val;
@ -150,7 +396,7 @@ QGstPlayer::onVolumeChanged(QGstPlayer *player)
} }
void void
QGstPlayer::onMuteChanged(QGstPlayer *player) Player::onMuteChanged(Player *player)
{ {
bool new_val; bool new_val;
@ -160,15 +406,8 @@ QGstPlayer::onMuteChanged(QGstPlayer *player)
} }
void 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(); bool val = player->isVideoAvailable();
if (player->videoAvailable_ != val) { if (player->videoAvailable_ != val) {
@ -176,10 +415,12 @@ QGstPlayer::onMediaInfoUpdated(QGstPlayer *player, GstPlayerMediaInfo *media_inf
emit player->videoAvailableChanged(val); emit player->videoAvailableChanged(val);
} }
player->mediaInfo()->update(media_info);
emit player->mediaInfoChanged(); emit player->mediaInfoChanged();
} }
QUrl QGstPlayer::source() const QUrl Player::source() const
{ {
Q_ASSERT(player_ != 0); Q_ASSERT(player_ != 0);
QString url = QString::fromLocal8Bit(gst_player_get_uri(player_)); QString url = QString::fromLocal8Bit(gst_player_get_uri(player_));
@ -187,90 +428,90 @@ QUrl QGstPlayer::source() const
return QUrl(url); return QUrl(url);
} }
qint64 QGstPlayer::duration() const qint64 Player::duration() const
{ {
Q_ASSERT(player_ != 0); Q_ASSERT(player_ != 0);
return gst_player_get_duration(player_); return gst_player_get_duration(player_);
} }
qint64 QGstPlayer::position() const qint64 Player::position() const
{ {
Q_ASSERT(player_ != 0); Q_ASSERT(player_ != 0);
return gst_player_get_position(player_); return gst_player_get_position(player_);
} }
qreal QGstPlayer::volume() const qreal Player::volume() const
{ {
Q_ASSERT(player_ != 0); Q_ASSERT(player_ != 0);
return gst_player_get_volume(player_); return gst_player_get_volume(player_);
} }
bool QGstPlayer::isMuted() const bool Player::isMuted() const
{ {
Q_ASSERT(player_ != 0); Q_ASSERT(player_ != 0);
return gst_player_get_mute(player_); return gst_player_get_mute(player_);
} }
int QGstPlayer::buffering() const int Player::buffering() const
{ {
return 0; return 0;
} }
QSize QGstPlayer::resolution() const QSize Player::resolution() const
{ {
return videoDimensions_; return videoDimensions_;
} }
void QGstPlayer::setResolution(QSize size) void Player::setResolution(QSize size)
{ {
videoDimensions_ = size; videoDimensions_ = size;
} }
QGstPlayer::State QGstPlayer::state() const Player::State Player::state() const
{ {
return state_; return state_;
} }
GstElement *QGstPlayer::pipeline() const GstElement *Player::pipeline() const
{ {
Q_ASSERT(player_ != 0); Q_ASSERT(player_ != 0);
return gst_player_get_pipeline(player_); return gst_player_get_pipeline(player_);
} }
void QGstPlayer::play() void Player::play()
{ {
Q_ASSERT(player_ != 0); Q_ASSERT(player_ != 0);
gst_player_play(player_); gst_player_play(player_);
} }
void QGstPlayer::pause() void Player::pause()
{ {
Q_ASSERT(player_ != 0); Q_ASSERT(player_ != 0);
gst_player_pause(player_); gst_player_pause(player_);
} }
void QGstPlayer::stop() void Player::stop()
{ {
Q_ASSERT(player_ != 0); Q_ASSERT(player_ != 0);
gst_player_stop(player_); gst_player_stop(player_);
} }
void QGstPlayer::seek(qint64 position) void Player::seek(qint64 position)
{ {
Q_ASSERT(player_ != 0); Q_ASSERT(player_ != 0);
gst_player_seek(player_, position); gst_player_seek(player_, position);
} }
void QGstPlayer::setSource(QUrl const& url) void Player::setSource(QUrl const& url)
{ {
Q_ASSERT(player_ != 0); Q_ASSERT(player_ != 0);
QByteArray uri = url.toString().toLocal8Bit(); QByteArray uri = url.toString().toLocal8Bit();
@ -280,43 +521,45 @@ void QGstPlayer::setSource(QUrl const& url)
emit sourceChanged(url); emit sourceChanged(url);
} }
void QGstPlayer::setVolume(qreal val) void Player::setVolume(qreal val)
{ {
Q_ASSERT(player_ != 0); Q_ASSERT(player_ != 0);
gst_player_set_volume(player_, val); gst_player_set_volume(player_, val);
} }
void QGstPlayer::setMuted(bool val) void Player::setMuted(bool val)
{ {
Q_ASSERT(player_ != 0); Q_ASSERT(player_ != 0);
gst_player_set_mute(player_, val); gst_player_set_mute(player_, val);
} }
void QGstPlayer::setPosition(qint64 pos) void Player::setPosition(qint64 pos)
{ {
Q_ASSERT(player_ != 0); Q_ASSERT(player_ != 0);
gst_player_seek(player_, pos); gst_player_seek(player_, pos);
} }
GstPlayerVideoRenderer *QGstPlayer::VideoRenderer::renderer() GstPlayerVideoRenderer *VideoRenderer::renderer()
{ {
return renderer_; return renderer_;
} }
QGstPlayer::VideoRenderer::VideoRenderer() VideoRenderer::VideoRenderer()
{ {
renderer_ = static_cast<GstPlayerVideoRenderer*> renderer_ = static_cast<GstPlayerVideoRenderer*>
(g_object_new (GST_TYPE_PLAYER_QT_VIDEO_RENDERER, "renderer", this, NULL)); (g_object_new (GST_TYPE_PLAYER_QT_VIDEO_RENDERER, "renderer", this, NULL));
} }
QGstPlayer::VideoRenderer::~VideoRenderer() VideoRenderer::~VideoRenderer()
{ {
if (renderer_) gst_object_unref(renderer_); if (renderer_) gst_object_unref(renderer_);
} }
}
struct _GstPlayerQtVideoRenderer struct _GstPlayerQtVideoRenderer
{ {
GObject parent; GObject parent;
@ -527,7 +770,7 @@ static void
qt_signal_dispatcher_param_specs qt_signal_dispatcher_param_specs
[QT_SIGNAL_DISPATCHER_PROP_PLAYER] = [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)); static_cast<GParamFlags>(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_properties (gobject_class, g_object_class_install_properties (gobject_class,

View file

@ -28,7 +28,16 @@
#include <QtQml/QQmlPropertyMap> #include <QtQml/QQmlPropertyMap>
#include <gst/player/player.h> #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_OBJECT
Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged) 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(State state READ state NOTIFY stateChanged)
Q_PROPERTY(QObject *mediaInfo READ mediaInfo NOTIFY mediaInfoChanged) Q_PROPERTY(QObject *mediaInfo READ mediaInfo NOTIFY mediaInfoChanged)
Q_PROPERTY(bool videoAvailable READ isVideoAvailable NOTIFY videoAvailableChanged) 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) Q_ENUMS(State)
public: public:
explicit Player(QObject *parent = 0, VideoRenderer *renderer = 0);
class VideoRenderer;
explicit QGstPlayer(QObject *parent = 0, VideoRenderer *renderer = 0);
typedef GstPlayerError Error; typedef GstPlayerError Error;
enum State { enum State {
@ -58,29 +69,6 @@ public:
PLAYING = GST_PLAYER_STATE_PLAYING 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; QUrl source() const;
qint64 duration() const; qint64 duration() const;
qint64 position() const; qint64 position() const;
@ -92,8 +80,11 @@ public:
QSize resolution() const; QSize resolution() const;
void setResolution(QSize size); void setResolution(QSize size);
bool isVideoAvailable() const; bool isVideoAvailable() const;
QQmlPropertyMap *mediaInfo() const; MediaInfo *mediaInfo() const;
QVariant currentVideo() const;
QVariant currentAudio() const;
QVariant currentSubtitle() const;
bool isSubtitleEnabled() const;
signals: signals:
void stateChanged(State new_state); void stateChanged(State new_state);
@ -107,6 +98,7 @@ signals:
void mediaInfoChanged(); void mediaInfoChanged();
void sourceChanged(QUrl new_url); void sourceChanged(QUrl new_url);
void videoAvailableChanged(bool videoAvailable); void videoAvailableChanged(bool videoAvailable);
void subtitleEnabledChanged(bool enabled);
public slots: public slots:
void play(); void play();
@ -117,27 +109,144 @@ public slots:
void setVolume(qreal val); void setVolume(qreal val);
void setMuted(bool val); void setMuted(bool val);
void setPosition(qint64 pos); void setPosition(qint64 pos);
void setCurrentVideo(QVariant track);
void setCurrentAudio(QVariant track);
void setCurrentSubtitle(QVariant track);
void setSubtitleEnabled(bool enabled);
private: private:
Q_DISABLE_COPY(QGstPlayer) Q_DISABLE_COPY(Player)
static void onStateChanged(QGstPlayer *, GstPlayerState state); static void onStateChanged(Player *, GstPlayerState state);
static void onPositionUpdated(QGstPlayer *, GstClockTime position); static void onPositionUpdated(Player *, GstClockTime position);
static void onDurationChanged(QGstPlayer *, GstClockTime duration); static void onDurationChanged(Player *, GstClockTime duration);
static void onBufferingChanged(QGstPlayer *, int percent); static void onBufferingChanged(Player *, int percent);
static void onVideoDimensionsChanged(QGstPlayer *, int w, int h); static void onVideoDimensionsChanged(Player *, int w, int h);
static void onVolumeChanged(QGstPlayer *); static void onVolumeChanged(Player *);
static void onMuteChanged(QGstPlayer *); static void onMuteChanged(Player *);
static void onMediaInfoUpdated(QGstPlayer *, GstPlayerMediaInfo *media_info); static void onMediaInfoUpdated(Player *, GstPlayerMediaInfo *media_info);
GstPlayer *player_; GstPlayer *player_;
State state_; State state_;
QSize videoDimensions_; QSize videoDimensions_;
QQmlPropertyMap *mediaInfoMap_; MediaInfo *mediaInfo_;
bool videoAvailable_; bool videoAvailable_;
bool subtitleEnabled_;
}; };
Q_DECLARE_METATYPE(QGstPlayer*) class VideoRenderer
Q_DECLARE_METATYPE(QGstPlayer::State) {
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" { extern "C" {