diff --git a/playback/player/qt/deployment.pri b/playback/player/qt/deployment.pri new file mode 100644 index 0000000000..5441b63dc8 --- /dev/null +++ b/playback/player/qt/deployment.pri @@ -0,0 +1,27 @@ +android-no-sdk { + target.path = /data/user/qt + export(target.path) + INSTALLS += target +} else:android { + x86 { + target.path = /libs/x86 + } else: armeabi-v7a { + target.path = /libs/armeabi-v7a + } else { + target.path = /libs/armeabi + } + export(target.path) + INSTALLS += target +} else:unix { + isEmpty(target.path) { + qnx { + target.path = /tmp/$${TARGET}/bin + } else { + target.path = /opt/$${TARGET}/bin + } + export(target.path) + } + INSTALLS += target +} + +export(INSTALLS) diff --git a/playback/player/qt/fontawesome-webfont.ttf b/playback/player/qt/fontawesome-webfont.ttf new file mode 100644 index 0000000000..ed9372f8ea Binary files /dev/null and b/playback/player/qt/fontawesome-webfont.ttf differ diff --git a/playback/player/qt/fontawesome-webfont.ttf.txt b/playback/player/qt/fontawesome-webfont.ttf.txt new file mode 100644 index 0000000000..946b3cb1b1 --- /dev/null +++ b/playback/player/qt/fontawesome-webfont.ttf.txt @@ -0,0 +1,10 @@ + +Font Awesome + +URL: http://fontawesome.io + +Font License + +License: SIL OFL 1.1 +URL: http://scripts.sil.org/OFL + diff --git a/playback/player/qt/fontawesome.js b/playback/player/qt/fontawesome.js new file mode 100644 index 0000000000..8d135e13b1 --- /dev/null +++ b/playback/player/qt/fontawesome.js @@ -0,0 +1,214 @@ +var Icon = { + Glass : "\uf000", + Music : "\uf001", + Search : "\uf002", + Envelope : "\uf003", + Heart : "\uf004", + Star : "\uf005", + StarEmpty : "\uf006", + User : "\uf007", + Film : "\uf008", + ThLarge : "\uf009", + Th : "\uf00a", + ThList : "\uf00b", + Ok : "\uf00c", + Remove : "\uf00d", + ZoomIn : "\uf00e", + ZoomOut : "\uf010", + Off : "\uf011", + Signal : "\uf012", + Cog : "\uf013", + Trash : "\uf014", + Home : "\uf015", + File : "\uf016", + Time : "\uf017", + Road : "\uf018", + DownloadAlt : "\uf019", + Download : "\uf01a", + Upload : "\uf01b", + Inbox : "\uf01c", + PlayCircle : "\uf01d", + Repeat : "\uf01e", + Refresh : "\uf021", + ListAlt : "\uf022", + Lock : "\uf023", + Flag : "\uf024", + Headphones : "\uf025", + VolumeOff : "\uf026", + VolumeDown : "\uf027", + VolumeUp : "\uf028", + Qrcode : "\uf029", + Barcode : "\uf02a", + Tag : "\uf02b", + Tags : "\uf02c", + Book : "\uf02d", + Bookmark : "\uf02e", + Print : "\uf02f", + Camera : "\uf030", + Font : "\uf031", + Bold : "\uf032", + Italic : "\uf033", + TextHeight : "\uf034", + TextWidth : "\uf035", + AlignLeft : "\uf036", + AlignCenter : "\uf037", + AlignRight : "\uf038", + AlignJustify : "\uf039", + List : "\uf03a", + IndentLeft : "\uf03b", + IndentRight : "\uf03c", + FacetimeVideo : "\uf03d", + Picture : "\uf03e", + Pencil : "\uf040", + MapMarker : "\uf041", + Adjust : "\uf042", + Tint : "\uf043", + Edit : "\uf044", + Share : "\uf045", + Check : "\uf046", + Move : "\uf047", + StepBackward : "\uf048", + StepForward : "\uf049", + Backward : "\uf04a", + Play : "\uf04b", + Pause : "\uf04c", + Stop : "\uf04d", + Forward : "\uf04e", + FastForward : "\uf050", + StepForward : "\uf051", + Eject : "\uf052", + ChevronLeft : "\uf053", + ChevronRight : "\uf054", + PlusSign : "\uf055", + MinusSign : "\uf056", + RemoveSign : "\uf057", + OkSign : "\uf058", + QuestionSign : "\uf059", + InfoSign : "\uf05a", + Screenshot : "\uf05b", + RemoveCircle : "\uf05c", + OkCircle : "\uf05d", + BanCircle : "\uf05e", + ArrowLeft : "\uf060", + ArrowRight : "\uf061", + ArrowUp : "\uf062", + ArrowDown : "\uf063", + ShareAlt : "\uf064", + ResizeFull : "\uf065", + ResizeSmall : "\uf066", + Plus : "\uf067", + Minus : "\uf068", + Asterish : "\uf069", + ExclamationSign : "\uf06a", + Gift : "\uf06b", + Leave : "\uf06c", + Fire : "\uf06d", + EyeOpen : "\uf06e", + EyeClose : "\uf070", + WarningSign : "\uf071", + Plane : "\uf072", + Calendar : "\uf073", + Random : "\uf074", + Comment : "\uf075", + Magnet : "\uf076", + ChevronUp : "\uf077", + ChevronDown : "\uf078", + Retweet : "\uf079", + ShoppingCart : "\uf07a", + FolderClose : "\uf07b", + FolderOpen : "\uf07c", + ResizeVertical : "\uf07d", + ResizeHorizontal : "\uf07e", + BarChart : "\uf080", + TwitterSign : "\uf081", + FacebookSign : "\uf082", + CameraRetro : "\uf083", + Key : "\uf084", + Cogs : "\uf085", + Comments : "\uf086", + ThumbsUp : "\uf087", + ThumbsDown : "\uf088", + StarHalf : "\uf089", + HeartEmpty : "\uf08a", + Signout : "\uf08b", + LinkedinSign : "\uf08c", + Pushpin : "\uf08d", + ExternalLink : "\uf08e", + Signin : "\uf090", + Trophy : "\uf091", + GithubSign : "\uf092", + UploadAlt : "\uf093", + Lemon : "\uf094", + Phone : "\uf095", + CheckEmpty : "\uf096", + BookmarkEmpty : "\uf097", + PhoneSign : "\uf098", + Twitter : "\uf099", + Facebook : "\uf09a", + Github : "\uf09b", + Unlock : "\uf09c", + CreditCard : "\uf09d", + Rss : "\uf09e", + Hdd : "\uf0a0", + Bullhorn : "\uf0a1", + Bell : "\uf0a2", + Certificate : "\uf0a3", + HandRight : "\uf0a4", + HandLeft : "\uf0a5", + HandUp : "\uf0a6", + HandDown : "\uf0a7", + CircleArrowLeft : "\uf0a8", + CircleArrowRight : "\uf0a9", + CircleArrowUp : "\uf0aa", + CircleArrowDown : "\uf0ab", + Globe : "\uf0ac", + Wrench : "\uf0ad", + Tasks : "\uf0ae", + Filter : "\uf0b0", + Briefcase : "\uf0b1", + Fullscreen : "\uf0b2", + Group : "\uf0c0", + Link : "\uf0c1", + Cloud : "\uf0c2", + Beaker : "\uf0c3", + Cut : "\uf0c4", + Copy : "\uf0c5", + PaperClip : "\uf0c6", + Save : "\uf0c7", + SignBlank : "\uf0c8", + Reorder : "\uf0c9", + ListUl : "\uf0ca", + ListOl : "\uf0cb", + Strikethrough : "\uf0cc", + Underline : "\uf0cd", + Table : "\uf0ce", + Magic : "\uf0d0", + Truck : "\uf0d1", + Pinterest : "\uf0d2", + PinterestSign : "\uf0d3", + GooglePlusSign : "\uf0d4", + GooglePlus : "\uf0d5", + Money : "\uf0d6", + CaretDown : "\uf0d7", + CaretUp : "\uf0d8", + CaretLeft : "\uf0d9", + CaretRight : "\uf0da", + Columns : "\uf0db", + Sort : "\uf0dc", + SortDown : "\uf0dd", + SortUp : "\uf0de", + EnvelopeAlt : "\uf0e0", + Linkedin : "\uf0e1", + Undo : "\uf0e2", + Legal : "\uf0e3", + Dashboard : "\uf0e4", + CommentAlt : "\uf0e5", + CommentsAlt : "\uf0e6", + Bolt : "\uf0e7", + Sitemap : "\uf0e8", + Unbrella : "\uf0e9", + Paste : "\uf0ea", + ClosedCaptions : "\uf20a", + UserMd : "\uf200", +}; + diff --git a/playback/player/qt/main.cpp b/playback/player/qt/main.cpp new file mode 100644 index 0000000000..5634732f6f --- /dev/null +++ b/playback/player/qt/main.cpp @@ -0,0 +1,52 @@ +/* GStreamer + * + * Copyright (C) 2015 Alexandre Moreno + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include +#include + +#include "player.h" + +int main(int argc, char *argv[]) +{ + QGuiApplication app(argc, argv); + + qmlRegisterType("Player", 1, 0, "Player"); + + /* the plugin must be loaded before loading the qml file to register the + * GstGLVideoItem qml item + * FIXME Add a QQmlExtensionPlugin into qmlglsink to register GstGLVideoItem + * with the QML engine, then remove this */ + gst_init(NULL,NULL); + GstElement *sink = gst_element_factory_make ("qmlglsink", NULL); + gst_object_unref(sink); + + + QQmlApplicationEngine engine; + engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); + + QObject *rootObject = engine.rootObjects().first(); + + Player *player = rootObject->findChild("player"); + QQuickItem *videoItem = rootObject->findChild("videoItem"); + player->setVideoOutput(videoItem); + + + return app.exec(); +} diff --git a/playback/player/qt/main.qml b/playback/player/qt/main.qml new file mode 100644 index 0000000000..c7fe42435e --- /dev/null +++ b/playback/player/qt/main.qml @@ -0,0 +1,461 @@ +/* GStreamer + * + * Copyright (C) 2015 Alexandre Moreno + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.4 +import QtQuick.Controls 1.1 +import QtQuick.Controls.Styles 1.3 +import QtQuick.Dialogs 1.2 +import QtQuick.Window 2.1 +import Player 1.0 +import org.freedesktop.gstreamer.GLVideoItem 1.0 + +import "fontawesome.js" as FontAwesome + +ApplicationWindow { + id: window + visible: true + width: 640 + height: 480 + x: 30 + y: 30 + color: "black" +// title : player.mediaInfo.title + + Player { + id: player + objectName: "player" + volume: 0.5 + onStateChanged: { + if (state === Player.STOPPED) { + playbutton.text = FontAwesome.Icon.Play + } + } + onResolutionChanged: { + if (player.videoAvailable) { + window.width = resolution.width + window.height = resolution.height + } + } + } + + GstGLVideoItem { + id: video + objectName: "videoItem" + anchors.centerIn: parent + width: 640 + height: 480 + } + + FileDialog { + id: fileDialog + //nameFilters: [TODO globs from mime types] + onAccepted: player.source = fileUrl + } + + Action { + id: fileOpenAction + text: "Open" + onTriggered: fileDialog.open() + } + + menuBar: MenuBar { + Menu { + title: "&File" + MenuItem { action: fileOpenAction } + MenuItem { text: "Quit"; onTriggered: Qt.quit() } + } + } + + Item { + anchors.fill: parent + FontLoader { + source: "fonts/fontawesome-webfont.ttf" + } + + Rectangle { + id : playbar + color: Qt.rgba(1, 1, 1, 0.7) + border.width: 1 + border.color: "white" + anchors.bottom: parent.bottom + anchors.bottomMargin: 15 + anchors.horizontalCenter: parent.horizontalCenter + width : grid.width + 20 + height: 40//childrenRect.height + 20 + radius: 5 + + MouseArea { + id: mousearea + anchors.fill: parent + hoverEnabled: true + onEntered: { + parent.opacity = 1.0 + hidetimer.start() + } + } + + Timer { + id: hidetimer + interval: 10000 + onTriggered: { + parent.opacity = 0.0 + stop() + } + } + + Grid { + id: grid + anchors.horizontalCenter: parent.horizontalCenter +// anchors.top: parent.top +// anchors.topMargin: 5 + + spacing: 7 + rows: 1 + verticalItemAlignment: Qt.AlignVCenter + + Text { + id : openmedia + font.pointSize: 17 + font.family: "FontAwesome" + text: FontAwesome.Icon.FolderOpen + + MouseArea { + anchors.fill: parent + onPressed: fileDialog.open() + } + } + + Item { + width: 17 + height: 17 + + Text { + anchors.centerIn: parent + font.pointSize: 17 + font.family: "FontAwesome" + text: FontAwesome.Icon.StepBackward + } + } + + Item { + width: 25 + height: 25 + + Text { + anchors.centerIn: parent + id : playbutton + font.pointSize: 25 + font.family: "FontAwesome" + //font.weight: Font.Light + text: FontAwesome.Icon.PlayCircle + } + + MouseArea { + id: playArea + anchors.fill: parent + onPressed: { + if (player.state !== Player.PLAYING) { + player.play() + playbutton.text = FontAwesome.Icon.Pause + playbutton.font.pointSize = 17 + } else { + player.pause() + playbutton.text = FontAwesome.Icon.PlayCircle + playbutton.font.pointSize = 25 + } + } + } + } + + Item { + width: 17 + height: 17 + + Text { + anchors.centerIn: parent + font.pointSize: 17 + font.family: "FontAwesome" + text: FontAwesome.Icon.StepForward + } + } + + Item { + width: 40 + height: 17 + Text { + id: timelabel + anchors.centerIn: parent + font.pointSize: 13 + color: "black" + text: { + var current = new Date(Math.floor(slider.value / 1e6)); + current.getMinutes() + ":" + ('0'+current.getSeconds()).slice(-2) + } + } + } + + Item { + width: 200 + height: 38 + Text { + anchors.centerIn: parent + text: player.mediaInfo.title + font.pointSize: 15 + } + } + + + + Item { + width: 40 + height: 17 + Text { + id: durationlabel + anchors.centerIn: parent + font.pointSize: 13 + color: "black" + text: { + var duration = new Date(Math.floor(player.duration / 1e6)); + duration.getMinutes() + ":" + ('0'+duration.getSeconds()).slice(-2) + } + } + } + + Item { + width: 17 + height: 17 + + + Text { + id : volume + anchors.centerIn: parent + font.pointSize: 17 + font.family: "FontAwesome" + text: { + if (volumeslider.value > volumeslider.maximumValue / 2) { + FontAwesome.Icon.VolumeUp + } else if (volumeslider.value === 0) { + FontAwesome.Icon.VolumeOff + } else { + FontAwesome.Icon.VolumeDown + } + } + } + + Rectangle { + id : volumebar + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: parent.top + //anchors.bottomMargin:3 + color: "lightgray" + width: 17 + height: 66 + visible: false + radius: 5 + + Slider { + id: volumeslider + value: player.volume + minimumValue: 0.0 + maximumValue: 1.0 + stepSize: 0.001 + anchors.centerIn: parent + orientation: Qt.Vertical + onPressedChanged: player.volume = value + + style: SliderStyle { + groove: Item { + implicitWidth: 47 + implicitHeight: 3 + anchors.centerIn: parent + + Rectangle { + antialiasing: true + height: parent.height + width: parent.width + color: "gray" + opacity: 0.8 + radius: 5 + + Rectangle { + antialiasing: true + height: parent.height + width: parent.width * control.value / control.maximumValue + color: "red" + radius: 5 + } + } + } + handle: Rectangle { + anchors.centerIn: parent + color: control.pressed ? "white" : "lightgray" + border.color: "gray" + border.width: 1 + implicitWidth: 11 + implicitHeight: 11 + radius: 90 + } + } + } + } + + MouseArea { + anchors.fill: parent + onPressed: { + volumebar.visible = !volumebar.visible + } + } + + MouseArea { + anchors.fill: volumebar + hoverEnabled: true + propagateComposedEvents: true + + onClicked: mouse.accepted = false; + onPressed: mouse.accepted = false; + onReleased: mouse.accepted = false; + onDoubleClicked: mouse.accepted = false; + onPositionChanged: mouse.accepted = false; + onPressAndHold: mouse.accepted = false; + + onExited: { + volumebar.visible = false + } + } + + } + + Text { + id: sub + font.pointSize: 17 + font.family: "FontAwesome" + text: FontAwesome.Icon.ClosedCaptions + } + + Text { + id : fullscreen + font.pointSize: 17 + font.family: "FontAwesome" + text: FontAwesome.Icon.ResizeFull + + MouseArea { + anchors.fill: parent + onClicked: { + if (window.visibility === Window.FullScreen) { + window.showNormal() + fullscreen.text = FontAwesome.Icon.ResizeFull + } else { + window.showFullScreen() + fullscreen.text = FontAwesome.Icon.ResizeSmall + } + } + } + } + } + + Item { + width: playbar.width + height: 5 + anchors.bottom: playbar.bottom + + Slider { + id: slider + maximumValue: player.duration + value: player.position + onPressedChanged: player.seek(value) + enabled: player.mediaInfo.isSeekable + anchors.bottom: parent.bottom + anchors.horizontalCenter: parent.horizontalCenter + + MouseArea { + id: sliderMouseArea + anchors.fill: parent + hoverEnabled: true + propagateComposedEvents: true + + onClicked: mouse.accepted = false; + onPressed: mouse.accepted = false; + onReleased: mouse.accepted = false; + onDoubleClicked: mouse.accepted = false; + onPositionChanged: mouse.accepted = false; + onPressAndHold: mouse.accepted = false; + } + + Rectangle { + id: hoveredcliptime + width: 40 + height: 17 + color: "lightgray" + anchors.verticalCenter: parent.verticalCenter + visible: sliderMouseArea.containsMouse + x: sliderMouseArea.mouseX + + Text { + font.pointSize: 13 + color: "black" + anchors.centerIn: parent + text: { + var value = (sliderMouseArea.mouseX - slider.x) * player.duration / (slider.width - slider.x) + var date = new Date(Math.floor(value / 1e6)); + date.getMinutes() + ":" + ('0' + date.getSeconds()).slice(-2) + } + } + } + + style: SliderStyle { + groove: Item { + implicitWidth: playbar.width + implicitHeight: 5 + + Rectangle { + height: parent.height + width: parent.width + anchors.verticalCenter: parent.verticalCenter + color: "gray" + opacity: 0.8 + + Rectangle { + antialiasing: true + color: "red" + height: parent.height + width: parent.width * control.value / control.maximumValue + } + + Rectangle { + antialiasing: true + color: "yellow" + height: parent.height + width: parent.width * player.buffering / 100 + } + } + } + handle: Rectangle { + anchors.centerIn: parent + color: control.pressed ? "white" : "lightgray" + implicitWidth: 4 + implicitHeight: 5 + } + } + } + } + + } + } +} diff --git a/playback/player/qt/play.pro b/playback/player/qt/play.pro new file mode 100644 index 0000000000..c8b29549b3 --- /dev/null +++ b/playback/player/qt/play.pro @@ -0,0 +1,53 @@ +TEMPLATE = app + +QT += qml quick widgets + +CONFIG += c++11 + +DEFINES += GST_USE_UNSTABLE_API + +INCLUDEPATH += ../lib + +RESOURCES += qml.qrc + +# Additional import path used to resolve QML modules in Qt Creator's code model +QML_IMPORT_PATH = + +# Default rules for deployment. +include(deployment.pri) + +# not tested (yet) +unix:!macx { +QT_CONFIG -= no-pkg-config +CONFIG += link_pkgconfig +PKGCONFIG = \ + gstreamer-1.0 \ + gstreamer-audio-1.0 \ + gstreamer-tag-1.0 \ + gstreamer-pbutils-1.0 \ + gstreamer-video-1.0 \ + gstreamer-gl-1.0 +} + +macx { + QMAKE_MAC_SDK = macosx10.9 + INCLUDEPATH += /Library/Frameworks/GStreamer.framework/Headers + + LIBS += \ + -framework AppKit \ + -F/Library/Frameworks -framework GStreamer +} + +HEADERS += \ + qgstplayer.h \ + player.h \ + quickrenderer.h + +SOURCES += main.cpp \ + qgstplayer.cpp \ + ../lib/gst/player/gstplayer.c \ + ../lib/gst/player/gstplayer-media-info.c \ + player.cpp \ + quickrenderer.cpp + +DISTFILES += diff --git a/playback/player/qt/player.cpp b/playback/player/qt/player.cpp new file mode 100644 index 0000000000..ec4b78cb28 --- /dev/null +++ b/playback/player/qt/player.cpp @@ -0,0 +1,41 @@ + +/* GStreamer + * + * Copyright (C) 2015 Alexandre Moreno + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "player.h" +#include "quickrenderer.h" + +Player::Player(QObject *parent) + : Player(parent, new QuickRenderer) +{ + +} + +Player::Player(QObject *parent, QuickRenderer *renderer) + : QGstPlayer(parent, renderer) + , renderer_(renderer) +{ + renderer_->setParent(this); +} + +void Player::setVideoOutput(QQuickItem *output) +{ + renderer_->setVideoItem(output); +} diff --git a/playback/player/qt/player.h b/playback/player/qt/player.h new file mode 100644 index 0000000000..88586f4e03 --- /dev/null +++ b/playback/player/qt/player.h @@ -0,0 +1,44 @@ +/* GStreamer + * + * Copyright (C) 2015 Alexandre Moreno + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef PLAYER_H +#define PLAYER_H + +#include +#include +#include "qgstplayer.h" + +class QuickRenderer; + +class Player : public QGstPlayer +{ + Q_OBJECT +public: + Player(QObject *parent = 0); + void setVideoOutput(QQuickItem *output); + +private: + Player(QObject *parent, QuickRenderer *renderer); + QuickRenderer *renderer_; +}; + +Q_DECLARE_METATYPE(Player*) + +#endif // PLAYER_H diff --git a/playback/player/qt/qgstplayer.cpp b/playback/player/qt/qgstplayer.cpp new file mode 100644 index 0000000000..c2fb3fbab7 --- /dev/null +++ b/playback/player/qt/qgstplayer.cpp @@ -0,0 +1,435 @@ +/* GStreamer + * + * Copyright (C) 2015 Alexandre Moreno + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "qgstplayer.h" +#include +#include + +#include + +class QGstPlayerRegisterMetaTypes +{ +public: + QGstPlayerRegisterMetaTypes() + { + qRegisterMetaType("State"); + } + +} _register; + +QGstPlayer::MediaInfo::MediaInfo(GstPlayerMediaInfo *media_info) + : mediaInfo_(media_info) +{ + +} + +QString QGstPlayer::MediaInfo::title() const +{ + QString title = QString::fromLocal8Bit + (gst_player_media_info_get_title(mediaInfo_)); + + // if media has no title, return the file name + if (title.isEmpty()) { + QUrl url(gst_player_media_info_get_uri(mediaInfo_)); + title = url.fileName(); + } + + return title; +} + +bool QGstPlayer::MediaInfo::isSeekable() const +{ + return gst_player_media_info_is_seekable(mediaInfo_); +} + +bool QGstPlayer::isVideoAvailable() const +{ + GstPlayerVideoInfo *video_info; + + video_info = gst_player_get_current_video_track (player_); + if (video_info) { + g_object_unref (video_info); + return true; + } + + return false; +} + +QQmlPropertyMap *QGstPlayer::mediaInfo() const +{ + return mediaInfoMap_; +} + +QGstPlayer::QGstPlayer(QObject *parent, QGstPlayer::VideoRenderer *renderer) + : QObject(parent) + , player_() + , state_(STOPPED) + , videoDimensions_(QSize()) + , mediaInfoMap_() + , videoAvailable_(false) +{ + + player_ = gst_player_new_full(renderer ? renderer->renderer() : 0, 0); + + 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); + + mediaInfoMap_ = new QQmlPropertyMap(this); +} + +void +QGstPlayer::onStateChanged(QGstPlayer * player, GstPlayerState state) +{ + player->state_ = static_cast(state); + + emit player->stateChanged(player->state_); +} + +void +QGstPlayer::onPositionUpdated(QGstPlayer * player, GstClockTime position) +{ + emit player->positionChanged(position); +} + +void +QGstPlayer::onDurationChanged(QGstPlayer * player, GstClockTime duration) +{ + emit player->durationChanged(duration); +} + +void +QGstPlayer::onBufferingChanged(QGstPlayer * player, int percent) +{ + emit player->bufferingChanged(percent); +} + +void +QGstPlayer::onVideoDimensionsChanged(QGstPlayer * player, int w, int h) +{ + QSize res(w,h); + + player->setResolution(res); + + emit player->resolutionChanged(res); +} + +void +QGstPlayer::onVolumeChanged(QGstPlayer *player) +{ + qreal new_val; + + new_val = gst_player_get_volume (player->player_); + + emit player->volumeChanged(new_val); +} + +void +QGstPlayer::onMuteChanged(QGstPlayer *player) +{ + bool new_val; + + new_val = gst_player_get_mute (player->player_); + + emit player->mutedChanged(new_val); +} + +void +QGstPlayer::onMediaInfoUpdated(QGstPlayer *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) { + player->videoAvailable_ = val; + emit player->videoAvailableChanged(val); + } + + emit player->mediaInfoChanged(); +} + +QUrl QGstPlayer::source() const +{ + Q_ASSERT(player_ != 0); + QString url = QString::fromLocal8Bit(gst_player_get_uri(player_)); + + return QUrl(url); +} + +qint64 QGstPlayer::duration() const +{ + Q_ASSERT(player_ != 0); + + return gst_player_get_duration(player_); +} + +qint64 QGstPlayer::position() const +{ + Q_ASSERT(player_ != 0); + + return gst_player_get_position(player_); +} + +qreal QGstPlayer::volume() const +{ + Q_ASSERT(player_ != 0); + + return gst_player_get_volume(player_); +} + +bool QGstPlayer::isMuted() const +{ + Q_ASSERT(player_ != 0); + + return gst_player_get_mute(player_); +} + +int QGstPlayer::buffering() const +{ + return 0; +} + +QSize QGstPlayer::resolution() const +{ + return videoDimensions_; +} + +void QGstPlayer::setResolution(QSize size) +{ + videoDimensions_ = size; +} + +QGstPlayer::State QGstPlayer::state() const +{ + return state_; +} + +GstElement *QGstPlayer::pipeline() const +{ + Q_ASSERT(player_ != 0); + + return gst_player_get_pipeline(player_); +} + +void QGstPlayer::play() +{ + Q_ASSERT(player_ != 0); + + gst_player_play(player_); +} + +void QGstPlayer::pause() +{ + Q_ASSERT(player_ != 0); + + gst_player_pause(player_); +} + +void QGstPlayer::stop() +{ + Q_ASSERT(player_ != 0); + + gst_player_stop(player_); +} + +void QGstPlayer::seek(qint64 position) +{ + Q_ASSERT(player_ != 0); + + gst_player_seek(player_, position); +} + +void QGstPlayer::setSource(QUrl const& url) +{ + Q_ASSERT(player_ != 0); + QByteArray uri = url.toString().toLocal8Bit(); + + gst_player_set_uri(player_, uri.data()); + + emit sourceChanged(url); +} + +void QGstPlayer::setVolume(qreal val) +{ + Q_ASSERT(player_ != 0); + + gst_player_set_volume(player_, val); +} + +void QGstPlayer::setMuted(bool val) +{ + Q_ASSERT(player_ != 0); + + gst_player_set_mute(player_, val); +} + +void QGstPlayer::setPosition(qint64 pos) +{ + Q_ASSERT(player_ != 0); + + gst_player_seek(player_, pos); +} + +GstPlayerVideoRenderer *QGstPlayer::VideoRenderer::renderer() +{ + return renderer_; +} + +QGstPlayer::VideoRenderer::VideoRenderer() +{ + renderer_ = static_cast + (g_object_new (GST_TYPE_PLAYER_QT_VIDEO_RENDERER, "renderer", this, NULL)); +} + +QGstPlayer::VideoRenderer::~VideoRenderer() +{ + if (renderer_) gst_object_unref(renderer_); +} + +struct _GstPlayerQtVideoRenderer +{ + GObject parent; + + gpointer renderer; +}; + +struct _GstPlayerQtVideoRendererClass +{ + GObjectClass parent_class; +}; + +static void +gst_player_qt_video_renderer_interface_init + (GstPlayerVideoRendererInterface * iface); + +G_DEFINE_TYPE_WITH_CODE (GstPlayerQtVideoRenderer, + gst_player_qt_video_renderer, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (GST_TYPE_PLAYER_VIDEO_RENDERER, + gst_player_qt_video_renderer_interface_init)) + +enum +{ + QT_VIDEO_RENDERER_PROP_0, + QT_VIDEO_RENDERER_PROP_RENDERER, + QT_VIDEO_RENDERER_PROP_LAST +}; + +static GParamSpec * qt_video_renderer_param_specs + [QT_VIDEO_RENDERER_PROP_LAST] = { NULL, }; + +static void +gst_player_qt_video_renderer_set_property (GObject * object, + guint prop_id, const GValue * value, GParamSpec * pspec) +{ + GstPlayerQtVideoRenderer *self = GST_PLAYER_QT_VIDEO_RENDERER (object); + + switch (prop_id) { + case QT_VIDEO_RENDERER_PROP_RENDERER: + qDebug() << "setting renderer"; + self->renderer = g_value_get_pointer (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_player_qt_video_renderer_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec) +{ + GstPlayerQtVideoRenderer *self = GST_PLAYER_QT_VIDEO_RENDERER (object); + + switch (prop_id) { + case QT_VIDEO_RENDERER_PROP_RENDERER: + g_value_set_pointer (value, self->renderer); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_player_qt_video_renderer_finalize (GObject * object) +{ + GstPlayerQtVideoRenderer *self = GST_PLAYER_QT_VIDEO_RENDERER (object); + + G_OBJECT_CLASS + (gst_player_qt_video_renderer_parent_class)->finalize(object); +} + +static void +gst_player_qt_video_renderer_class_init + (GstPlayerQtVideoRendererClass * klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->set_property = + gst_player_qt_video_renderer_set_property; + gobject_class->get_property = + gst_player_qt_video_renderer_get_property; + gobject_class->finalize = gst_player_qt_video_renderer_finalize; + + qt_video_renderer_param_specs + [QT_VIDEO_RENDERER_PROP_RENDERER] = + g_param_spec_pointer ("renderer", "Qt Renderer", "", + static_cast(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (gobject_class, + QT_VIDEO_RENDERER_PROP_LAST, + qt_video_renderer_param_specs); +} + +static void +gst_player_qt_video_renderer_init (GstPlayerQtVideoRenderer * self) +{ + +} + +static GstElement * +gst_player_qt_video_renderer_create_video_sink + (GstPlayerVideoRenderer * iface, GstPlayer *player) +{ + GstPlayerQtVideoRenderer *self = GST_PLAYER_QT_VIDEO_RENDERER (iface); + + g_assert(self->renderer != NULL); + + return static_cast(self->renderer)->createVideoSink(); +} + +static void +gst_player_qt_video_renderer_interface_init + (GstPlayerVideoRendererInterface * iface) +{ + iface->create_video_sink = gst_player_qt_video_renderer_create_video_sink; +} diff --git a/playback/player/qt/qgstplayer.h b/playback/player/qt/qgstplayer.h new file mode 100644 index 0000000000..aeb45a5d35 --- /dev/null +++ b/playback/player/qt/qgstplayer.h @@ -0,0 +1,162 @@ +/* GStreamer + * + * Copyright (C) 2015 Alexandre Moreno + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef QGSTPLAYER_H +#define QGSTPLAYER_H + +#include +#include +#include +//#include +#include +#include + +class QGstPlayer : public QObject +{ + Q_OBJECT + Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged) + Q_PROPERTY(qint64 duration READ duration NOTIFY durationChanged) + Q_PROPERTY(qint64 position READ position NOTIFY positionChanged) + Q_PROPERTY(qreal volume READ volume WRITE setVolume NOTIFY volumeChanged) + Q_PROPERTY(bool muted READ isMuted WRITE setMuted NOTIFY mutedChanged) + Q_PROPERTY(int buffering READ buffering NOTIFY bufferingChanged) + Q_PROPERTY(QSize resolution READ resolution WRITE setResolution NOTIFY resolutionChanged) + 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_ENUMS(State) + +public: + + class VideoRenderer; + + explicit QGstPlayer(QObject *parent = 0, VideoRenderer *renderer = 0); + + typedef GstPlayerError Error; + enum State { + STOPPED = GST_PLAYER_STATE_STOPPED, + BUFFERING = GST_PLAYER_STATE_BUFFERING, + PAUSED = GST_PLAYER_STATE_PAUSED, + 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; + qreal volume() const; + bool isMuted() const; + int buffering() const; + State state() const; + GstElement *pipeline() const; + QSize resolution() const; + void setResolution(QSize size); + bool isVideoAvailable() const; + QQmlPropertyMap *mediaInfo() const; + + +signals: + void stateChanged(State new_state); + void bufferingChanged(int percent); + void enfOfStream(); + void positionChanged(qint64 new_position); + void durationChanged(qint64 duration); + void resolutionChanged(QSize resolution); + void volumeChanged(qreal volume); + void mutedChanged(bool muted); + void mediaInfoChanged(); + void sourceChanged(QUrl new_url); + void videoAvailableChanged(bool videoAvailable); + +public slots: + void play(); + void pause(); + void stop(); + void seek(qint64 position); + void setSource(QUrl const& url); + void setVolume(qreal val); + void setMuted(bool val); + void setPosition(qint64 pos); + +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); + + GstPlayer *player_; + State state_; + QSize videoDimensions_; + QQmlPropertyMap *mediaInfoMap_; + bool videoAvailable_; +}; + +G_BEGIN_DECLS + +typedef struct _GstPlayerQtVideoRenderer + GstPlayerQtVideoRenderer; + +typedef struct _GstPlayerQtVideoRendererClass + GstPlayerQtVideoRendererClass; + +#define GST_TYPE_PLAYER_QT_VIDEO_RENDERER (gst_player_qt_video_renderer_get_type ()) +#define GST_IS_PLAYER_QT_VIDEO_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_PLAYER_QT_VIDEO_RENDERER)) +#define GST_IS_PLAYER_QT_VIDEO_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_PLAYER_QT_VIDEO_RENDERER)) +#define GST_PLAYER_QT_VIDEO_RENDERER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_PLAYER_QT_VIDEO_RENDERER, GstPlayerQtVideoRendererClass)) +#define GST_PLAYER_QT_VIDEO_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_PLAYER_QT_VIDEO_RENDERER, GstPlayerQtVideoRenderer)) +#define GST_PLAYER_QT_VIDEO_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_PLAYER_QT_VIDEO_RENDERER, GstPlayerQtVideoRendererClass)) +#define GST_PLAYER_QT_VIDEO_RENDERER_CAST(obj) ((GstPlayerQtVideoRenderer*)(obj)) + +GType gst_player_qt_video_renderer_get_type (void); + +G_END_DECLS + +Q_DECLARE_METATYPE(QGstPlayer*) +Q_DECLARE_METATYPE(QGstPlayer::State) + +#endif // QGSTPLAYER_H diff --git a/playback/player/qt/qml.qrc b/playback/player/qt/qml.qrc new file mode 100644 index 0000000000..105221780b --- /dev/null +++ b/playback/player/qt/qml.qrc @@ -0,0 +1,9 @@ + + + main.qml + fontawesome.js + + + fontawesome-webfont.ttf + + diff --git a/playback/player/qt/quickrenderer.cpp b/playback/player/qt/quickrenderer.cpp new file mode 100644 index 0000000000..5dd68533ec --- /dev/null +++ b/playback/player/qt/quickrenderer.cpp @@ -0,0 +1,56 @@ +/* GStreamer + * + * Copyright (C) 2015 Alexandre Moreno + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "quickrenderer.h" + +QuickRenderer::QuickRenderer(QObject *parent) + : QObject(parent) + , QGstPlayer::VideoRenderer() + , sink() +{ + +} + +QuickRenderer::~QuickRenderer() +{ + if (sink) gst_object_unref(sink); +} + +GstElement *QuickRenderer::createVideoSink() +{ + GstElement *qmlglsink = gst_element_factory_make("qmlglsink", NULL); + + GstElement *glsinkbin = gst_element_factory_make ("glsinkbin", NULL); + + Q_ASSERT(qmlglsink && glsinkbin); + + g_object_set (glsinkbin, "sink", qmlglsink, NULL); + + sink = static_cast(gst_object_ref_sink(qmlglsink)); + + return glsinkbin; +} + +void QuickRenderer::setVideoItem(QQuickItem *item) +{ + Q_ASSERT(item); + + g_object_set(sink, "widget", item, NULL); +} diff --git a/playback/player/qt/quickrenderer.h b/playback/player/qt/quickrenderer.h new file mode 100644 index 0000000000..99519fbd8b --- /dev/null +++ b/playback/player/qt/quickrenderer.h @@ -0,0 +1,42 @@ +/* GStreamer + * + * Copyright (C) 2015 Alexandre Moreno + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef QUICKPLAYER_H +#define QUICKPLAYER_H + +#include +#include +#include "qgstplayer.h" + +class QuickRenderer : public QObject, public QGstPlayer::VideoRenderer +{ + Q_OBJECT +public: + QuickRenderer(QObject *parent = 0); + ~QuickRenderer(); + + GstElement *createVideoSink(); + void setVideoItem(QQuickItem *item); + +private: + GstElement *sink; +}; + +#endif // QUICKPLAYER_H