gstreamer/playback/player/qt/main.qml

714 lines
25 KiB
QML
Raw Normal View History

/* GStreamer
*
* Copyright (C) 2015 Alexandre Moreno <alexmorenocano@gmail.com>
*
* 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
positionUpdateInterval: 100
onStateChanged: {
if (state === Player.STOPPED) {
playbutton.state = "play"
}
}
onResolutionChanged: {
if (player.videoAvailable) {
window.width = resolution.width
window.height = resolution.height
}
}
}
GstGLVideoItem {
id: video
objectName: "videoItem"
anchors.centerIn: parent
width: parent.width
height: parent.height
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
onPositionChanged: {
playbar.opacity = 1.0
hidetimer.start()
}
}
Timer {
id: hidetimer
interval: 5000
onTriggered: {
playbar.opacity = 0.0
settings.visible = false
stop()
}
}
FileDialog {
id: fileDialog
//nameFilters: [TODO globs from mime types]
onAccepted: player.source = fileUrl
}
Action {
id: fileOpenAction
text: "Open"
onTriggered: fileDialog.open()
}
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
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.pixelSize: 13
anchors.centerIn: parent
}
Text {
font.pixelSize: 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.pixelSize: 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.pixelSize: 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.pixelSize: 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
spacing: 7
rows: 1
verticalItemAlignment: Qt.AlignVCenter
Text {
id : openmedia
font.pixelSize: 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.pixelSize: 17
font.family: "FontAwesome"
text: FontAwesome.Icon.StepBackward
}
}
Item {
width: 25
height: 25
Text {
anchors.centerIn: parent
id : playbutton
font.family: "FontAwesome"
state: "play"
states: [
State {
name: "play"
PropertyChanges {
target: playbutton
text: FontAwesome.Icon.PlayCircle
font.pixelSize: 25
}
},
State {
name: "pause"
PropertyChanges {
target: playbutton
text: FontAwesome.Icon.Pause
font.pixelSize: 17
}
}
]
}
MouseArea {
id: playArea
anchors.fill: parent
onPressed: {
if (player.state !== Player.PLAYING) {
player.play()
playbutton.state = "pause"
} else {
player.pause()
playbutton.state = "play"
}
}
}
}
Item {
width: 17
height: 17
Text {
anchors.centerIn: parent
font.pixelSize: 17
font.family: "FontAwesome"
text: FontAwesome.Icon.StepForward
}
}
Item {
width: 40
height: 17
Text {
id: timelabel
anchors.centerIn: parent
font.pixelSize: 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
width: parent.width
text: player.mediaInfo.title
font.pixelSize: 15
elide: Text.ElideRight
}
}
Item {
width: 40
height: 17
Text {
id: durationlabel
anchors.centerIn: parent
font.pixelSize: 13
color: "black"
text: {
var duration = new Date(Math.floor(player.duration / 1e6));
duration.getMinutes() + ":" + ('0'+duration.getSeconds()).slice(-2)
}
}
}
Text {
id: sub
font.pixelSize: 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
font.pixelSize: 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: cog
font.pixelSize: 17
font.family: "FontAwesome"
text: FontAwesome.Icon.Cog
MouseArea {
anchors.fill: parent
onClicked: {
settings.visible = !settings.visible
videos.visible = false
audios.visible = false
subtitles.visible = false
}
}
}
Text {
id : fullscreen
font.pixelSize: 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.seekable
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.pixelSize: 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
}
}
}
}
}
}
}