Merge remote-tracking branch 'origin/5.4' into dev

Change-Id: I8b9177d90afac8b834d333efc6c22b6b35dceaf8
This commit is contained in:
Frederik Gladhorn
2014-10-09 18:04:44 +02:00
166 changed files with 3766 additions and 5995 deletions

View File

@@ -0,0 +1 @@
SOURCES += main.cpp

View File

@@ -1,6 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the Qt Toolkit.
@@ -39,22 +39,9 @@
**
****************************************************************************/
#include "qt7backend.h"
#include <linux/videodev2.h>
#import <Foundation/NSAutoreleasePool.h>
#include <CoreFoundation/CFBase.h>
QT_BEGIN_NAMESPACE
AutoReleasePool::AutoReleasePool()
int main(int argc, char** argv)
{
pool = (void*)[[NSAutoreleasePool alloc] init];
return 0;
}
AutoReleasePool::~AutoReleasePool()
{
[(NSAutoreleasePool*)pool release];
}
QT_END_NAMESPACE

View File

@@ -30,8 +30,11 @@
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef _WIN32_WCE
#include <wmp.h>
#else
#include <wmpcore.h>
#endif
int main(int, char**)
{

View File

@@ -3,4 +3,5 @@ CONFIG += console
SOURCES += main.cpp
LIBS += -lstrmiids -lole32 -lOleaut32 -luser32 -lgdi32
LIBS += -lstrmiids -lole32 -lOleaut32
!wince*:LIBS += -luser32 -lgdi32

65
dist/changes-5.3.2 vendored Normal file
View File

@@ -0,0 +1,65 @@
Qt 5.3.2 is a bug-fix release. It maintains both forward and backward
compatibility (source and binary) with Qt 5.3.0 and 5.3.1.
For more details, refer to the online documentation included in this
distribution. The documentation is also available online:
http://qt-project.org/doc/qt-5.3
The Qt version 5.3 series is binary compatible with the 5.2.x series.
Applications compiled for 5.2 will continue to run with 5.3.
Some of the changes listed in this file include issue tracking numbers
corresponding to tasks in the Qt Bug Tracker:
http://bugreports.qt-project.org/
Each of these identifiers can be entered in the bug tracker to obtain more
information about a particular change.
****************************************************************************
* Platform Specific Changes *
****************************************************************************
Android
-------
- Fixed regression causing videos recorded with the camera not to be registered with the Android
media scanner, making them invisible to media browsing apps.
- Fixed crash when unloading a QCamera while a recording is active.
- [QTBUG-39307] Setting camera parameters on the QML Camera type (e.g. digitalZoom, flashMode)
now works correctly when set before the camera is loaded.
- [QTBUG-40208] QAudioOutput::setNotifyInterval() can now be used when the output is active.
- [QTBUG-40274] Fixed metadata not being loaded by the MediaPlayer when playing a remote media,
from a qrc file or from assets.
iOS
---
- [QTBUG-39036] Audio played using SoundEffect or QAudioOutput is now correctly muted when the
device is set to silent mode or when the screen is locked.
- [QTBUG-39385] The last video frame displayed in a QML VideoOutput doesn't remain on screen
anymore after destroying the VideoOutput.
Linux
-----
- MediaPlayer's loops property now works correctly when playing a media from a qrc file.
- [QTBUG-29742] Fixed Qt apps hanging when audio APIs are used and PulseAudio is not running.
- [QTBUG-39949] Fixed QMediaRecorder::setOutputLocation() not working with QUrl::fromLocalFile().
OS X
----
- Application doesn't freeze anymore when changing the system audio output device while audio
is being played with QSoundEffect or QAudioOutput.
- [QTBUG-38666] Video frames are now correctly positioned on screen when playing a live stream
in a QVideoWidget. This also applies to iOS.
- [QTBUG-38668] Fixed crash when setting QMediaRecorder's output location to a URL containing
nonstandard characters.
Windows
-------
- The DirectShow camera backend has been almost entirely rewritten. It doesn't provide any new
feature but it now works as it should.

View File

@@ -315,20 +315,11 @@ void InputTest::initializeAudio()
void InputTest::createAudioInput()
{
m_audioInput = new QAudioInput(m_device, m_format, this);
connect(m_audioInput, SIGNAL(notify()), SLOT(notified()));
connect(m_audioInput, SIGNAL(stateChanged(QAudio::State)), SLOT(handleStateChanged(QAudio::State)));
m_volumeSlider->setValue(m_audioInput->volume() * 100);
m_audioInfo->start();
m_audioInput->start(m_audioInfo);
}
void InputTest::notified()
{
qWarning() << "bytesReady = " << m_audioInput->bytesReady()
<< ", " << "elapsedUSecs = " <<m_audioInput->elapsedUSecs()
<< ", " << "processedUSecs = "<<m_audioInput->processedUSecs();
}
void InputTest::readMore()
{
if (!m_audioInput)
@@ -364,27 +355,19 @@ void InputTest::toggleSuspend()
{
// toggle suspend/resume
if (m_audioInput->state() == QAudio::SuspendedState) {
qWarning() << "status: Suspended, resume()";
m_audioInput->resume();
m_suspendResumeButton->setText(tr(SUSPEND_LABEL));
} else if (m_audioInput->state() == QAudio::ActiveState) {
qWarning() << "status: Active, suspend()";
m_audioInput->suspend();
m_suspendResumeButton->setText(tr(RESUME_LABEL));
} else if (m_audioInput->state() == QAudio::StoppedState) {
qWarning() << "status: Stopped, resume()";
m_audioInput->resume();
m_suspendResumeButton->setText(tr(SUSPEND_LABEL));
} else if (m_audioInput->state() == QAudio::IdleState) {
qWarning() << "status: IdleState";
// no-op
}
}
void InputTest::handleStateChanged(QAudio::State state)
{
qWarning() << "state = " << state;
}
void InputTest::refreshDisplay()
{
m_canvas->setLevel(m_audioInfo->level());

View File

@@ -110,11 +110,9 @@ private:
private slots:
void refreshDisplay();
void notified();
void readMore();
void toggleMode();
void toggleSuspend();
void handleStateChanged(QAudio::State state);
void deviceChanged(int index);
void sliderChanged(int value);

View File

@@ -247,8 +247,6 @@ void AudioTest::createAudioOutput()
delete m_audioOutput;
m_audioOutput = 0;
m_audioOutput = new QAudioOutput(m_device, m_format, this);
connect(m_audioOutput, SIGNAL(notify()), SLOT(notified()));
connect(m_audioOutput, SIGNAL(stateChanged(QAudio::State)), SLOT(handleStateChanged(QAudio::State)));
m_generator->start();
m_audioOutput->start(m_generator);
m_volumeSlider->setValue(int(m_audioOutput->volume()*100.0f));
@@ -275,13 +273,6 @@ void AudioTest::volumeChanged(int value)
m_audioOutput->setVolume(qreal(value/100.0f));
}
void AudioTest::notified()
{
qWarning() << "bytesFree = " << m_audioOutput->bytesFree()
<< ", " << "elapsedUSecs = " << m_audioOutput->elapsedUSecs()
<< ", " << "processedUSecs = " << m_audioOutput->processedUSecs();
}
void AudioTest::pullTimerExpired()
{
if (m_audioOutput && m_audioOutput->state() != QAudio::StoppedState) {
@@ -319,23 +310,16 @@ void AudioTest::toggleMode()
void AudioTest::toggleSuspendResume()
{
if (m_audioOutput->state() == QAudio::SuspendedState) {
qWarning() << "status: Suspended, resume()";
m_audioOutput->resume();
m_suspendResumeButton->setText(tr(SUSPEND_LABEL));
} else if (m_audioOutput->state() == QAudio::ActiveState) {
qWarning() << "status: Active, suspend()";
m_audioOutput->suspend();
m_suspendResumeButton->setText(tr(RESUME_LABEL));
} else if (m_audioOutput->state() == QAudio::StoppedState) {
qWarning() << "status: Stopped, resume()";
m_audioOutput->resume();
m_suspendResumeButton->setText(tr(SUSPEND_LABEL));
} else if (m_audioOutput->state() == QAudio::IdleState) {
qWarning() << "status: IdleState";
// no-op
}
}
void AudioTest::handleStateChanged(QAudio::State state)
{
qWarning() << "state = " << state;
}

View File

@@ -110,11 +110,9 @@ private:
QByteArray m_buffer;
private slots:
void notified();
void pullTimerExpired();
void toggleMode();
void toggleSuspendResume();
void handleStateChanged(QAudio::State state);
void deviceChanged(int index);
void volumeChanged(int);
};

View File

@@ -114,6 +114,8 @@ AudioRecorder::AudioRecorder(QWidget *parent) :
SLOT(updateProgress(qint64)));
connect(audioRecorder, SIGNAL(statusChanged(QMediaRecorder::Status)), this,
SLOT(updateStatus(QMediaRecorder::Status)));
connect(audioRecorder, SIGNAL(stateChanged(QMediaRecorder::State)),
this, SLOT(onStateChanged(QMediaRecorder::State)));
connect(audioRecorder, SIGNAL(error(QMediaRecorder::Error)), this,
SLOT(displayErrorMessage()));
}
@@ -138,47 +140,44 @@ void AudioRecorder::updateStatus(QMediaRecorder::Status status)
switch (status) {
case QMediaRecorder::RecordingStatus:
if (audioLevels.count() != audioRecorder->audioSettings().channelCount()) {
qDeleteAll(audioLevels);
audioLevels.clear();
for (int i = 0; i < audioRecorder->audioSettings().channelCount(); ++i) {
QAudioLevel *level = new QAudioLevel(ui->centralwidget);
audioLevels.append(level);
ui->levelsLayout->addWidget(level);
}
}
ui->recordButton->setText(tr("Stop"));
ui->pauseButton->setText(tr("Pause"));
if (audioRecorder->outputLocation().isEmpty())
statusMessage = tr("Recording");
else
statusMessage = tr("Recording to %1").arg(
audioRecorder->outputLocation().toString());
statusMessage = tr("Recording to %1").arg(audioRecorder->actualLocation().toString());
break;
case QMediaRecorder::PausedStatus:
clearAudioLevels();
ui->recordButton->setText(tr("Stop"));
ui->pauseButton->setText(tr("Resume"));
statusMessage = tr("Paused");
break;
case QMediaRecorder::UnloadedStatus:
case QMediaRecorder::LoadedStatus:
clearAudioLevels();
ui->recordButton->setText(tr("Record"));
ui->pauseButton->setText(tr("Pause"));
statusMessage = tr("Stopped");
default:
break;
}
ui->pauseButton->setEnabled(audioRecorder->state()
!= QMediaRecorder::StoppedState);
if (audioRecorder->error() == QMediaRecorder::NoError)
ui->statusbar->showMessage(statusMessage);
}
void AudioRecorder::onStateChanged(QMediaRecorder::State state)
{
switch (state) {
case QMediaRecorder::RecordingState:
ui->recordButton->setText(tr("Stop"));
ui->pauseButton->setText(tr("Pause"));
break;
case QMediaRecorder::PausedState:
ui->recordButton->setText(tr("Stop"));
ui->pauseButton->setText(tr("Resume"));
break;
case QMediaRecorder::StoppedState:
ui->recordButton->setText(tr("Record"));
ui->pauseButton->setText(tr("Pause"));
break;
}
ui->pauseButton->setEnabled(audioRecorder->state() != QMediaRecorder::StoppedState);
}
static QVariant boxValue(const QComboBox *box)
{
int idx = box->currentIndex();
@@ -347,6 +346,16 @@ QVector<qreal> getBufferLevels(const T *buffer, int frames, int channels)
void AudioRecorder::processBuffer(const QAudioBuffer& buffer)
{
if (audioLevels.count() != buffer.format().channelCount()) {
qDeleteAll(audioLevels);
audioLevels.clear();
for (int i = 0; i < buffer.format().channelCount(); ++i) {
QAudioLevel *level = new QAudioLevel(ui->centralwidget);
audioLevels.append(level);
ui->levelsLayout->addWidget(level);
}
}
QVector<qreal> levels = getBufferLevels(buffer);
for (int i = 0; i < levels.count(); ++i)
audioLevels.at(i)->setLevel(levels.at(i));

View File

@@ -71,6 +71,7 @@ private slots:
void toggleRecord();
void updateStatus(QMediaRecorder::Status);
void onStateChanged(QMediaRecorder::State);
void updateProgress(qint64 pos);
void displayErrorMessage();

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

View File

@@ -46,7 +46,7 @@ The following image shows the application executing the video-overlay scene,
which creates a dummy overlay item (just a semi-transparent \l{Rectangle}),
which moves across the \l{VideoOutput} item.
\image qmlvideo-overlay.png
\image qmlvideo-overlay.jpg
\include examples-run.qdocinc
@@ -67,7 +67,7 @@ the following items:
average over the past second.
\endlist
\image qmlvideo-menu.png
\image qmlvideo-menu.jpg
Each scene in the flickable list is implemented in its own QML file - for
example the video-basic scene (which just displays a static \l{VideoOutput}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -1,35 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="10px"
height="46px"
version="1.1">
<g>
<defs>
<linearGradient id="MyGradient1" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="lightcyan" />
<stop offset="100%" stop-color="dodgerblue" />
</linearGradient>
</defs>
<rect
stroke="white"
fill="url(#MyGradient1)"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
width="8"
height="44"
x="1"
y="1"
rx="4"
ry="4"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -1,35 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="10px"
height="46px"
version="1.1">
<g>
<defs>
<linearGradient id="MyGradient1" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="skyblue" />
<stop offset="100%" stop-color="darkblue" />
</linearGradient>
</defs>
<rect
stroke="white"
fill="url(#MyGradient1)"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
width="8"
height="44"
x="1"
y="1"
rx="4"
ry="4"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -1,5 +0,0 @@
border.left: 10
border.top: 12
border.bottom: 12
border.right: 10
source: titlebar.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 662 B

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -123,13 +123,14 @@ int main(int argc, char *argv[])
const QStringList moviesLocation = QStandardPaths::standardLocations(QStandardPaths::MoviesLocation);
const QUrl videoPath =
QUrl::fromLocalFile(moviesLocation.isEmpty() ?
app.applicationDirPath() :
moviesLocation.front());
QUrl::fromLocalFile(moviesLocation.isEmpty() ?
app.applicationDirPath() :
moviesLocation.front());
viewer.rootContext()->setContextProperty("videoPath", videoPath);
QMetaObject::invokeMethod(rootObject, "init");
viewer.setMinimumSize(QSize(640, 360));
viewer.show();
return app.exec();

View File

@@ -33,30 +33,31 @@
import QtQuick 2.0
Rectangle {
Item {
id: root
color: textColor
radius: 0.25 * height
property string text
property color bgColor: "white"
property color bgColorSelected: "red"
property color textColor: "black"
property color bgColor: "#757575"
property color bgColorSelected: "#bdbdbd"
property color textColor: "white"
property color textColorSelected: "black"
property alias enabled: mouseArea.enabled
property alias radius: bgr.radius
signal clicked
Rectangle {
anchors { fill: parent; margins: 1 }
id: bgr
anchors.fill: parent
color: mouseArea.pressed ? bgColorSelected : bgColor
radius: 0.25 * height
radius: height / 15
Text {
id: text
anchors.centerIn: parent
text: root.text
font.pixelSize: 0.5 * parent.height
color: mouseArea.pressed ? bgColor : textColor
font.pixelSize: 0.4 * parent.height
color: mouseArea.pressed ? textColorSelected : textColor
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}

View File

@@ -97,21 +97,16 @@ Rectangle {
}
function initialize() {
console.log("[qmlvideo] Content.initialize: contentType " + contentType)
if ("video" == contentType) {
console.log("[qmlvideo] Content.initialize: loading VideoItem.qml")
contentLoader.source = "VideoItem.qml"
if (Loader.Error == contentLoader.status) {
console.log("[qmlvideo] Content.initialize: loading VideoDummy.qml")
contentLoader.source = "VideoDummy.qml"
dummy = true
}
contentLoader.item.volume = volume
} else if ("camera" == contentType) {
console.log("[qmlvideo] Content.initialize: loading CameraItem.qml")
contentLoader.source = "CameraItem.qml"
if (Loader.Error == contentLoader.status) {
console.log("[qmlvideo] Content.initialize: loading CameraDummy.qml")
contentLoader.source = "CameraDummy.qml"
dummy = true
}
@@ -127,12 +122,10 @@ Rectangle {
if (root.autoStart)
root.start()
}
console.log("[qmlvideo] Content.initialize: complete")
root.initialized()
}
function start() {
console.log("[qmlvideo] Content.start")
if (contentLoader.item) {
if (root.contentType == "video")
contentLoader.item.mediaSource = root.source
@@ -142,7 +135,6 @@ Rectangle {
}
function stop() {
console.log("[qmlvideo] Content.stop")
if (contentLoader.item) {
contentLoader.item.stop()
if (root.contentType == "video")

View File

@@ -1,42 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the Qt Mobility Components.
**
** $QT_BEGIN_LICENSE:LGPL21$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** $QT_END_LICENSE$
**
****************************************************************************/
import QtQuick 2.0
import QtSystemInfo 5.0
// NOTE: The QtSystemInfo module is not yet part of Qt 5
Item {
ScreenSaver {
screenSaverInhibited: true
}
}

View File

@@ -38,6 +38,8 @@ Rectangle {
color: "transparent"
opacity: 0.0
property alias enabled: mouseArea.enabled
property int dialogWidth: 300
property int dialogHeight: 200
state: enabled ? "on" : "baseState"
states: [
@@ -70,9 +72,9 @@ Rectangle {
Rectangle {
anchors.centerIn: parent
width: 300
height: 200
radius: 10
width: dialogWidth
height: dialogHeight
radius: 5
color: "white"
Text {

View File

@@ -40,6 +40,9 @@ Rectangle {
property string folder
property int itemHeight: Math.min(parent.width, parent.height) / 15
property int buttonHeight: Math.min(parent.width, parent.height) / 12
signal fileSelected(string file)
function selectFile(file) {
@@ -66,12 +69,12 @@ Rectangle {
Rectangle {
id: root
color: "white"
color: "black"
property bool showFocusHighlight: false
property variant folders: folders1
property variant view: view1
property alias folder: folders1.folder
property color textColor: "black"
property color textColor: "white"
FolderListModel {
id: folders1
@@ -103,34 +106,39 @@ Rectangle {
fileBrowser.selectFile(path)
}
width: root.width
height: 52
height: folderImage.height
color: "transparent"
Rectangle {
id: highlight; visible: false
id: highlight
visible: false
anchors.fill: parent
color: palette.highlight
gradient: Gradient {
GradientStop { id: t1; position: 0.0; color: palette.highlight }
GradientStop { id: t2; position: 1.0; color: Qt.lighter(palette.highlight) }
}
anchors.leftMargin: 5
anchors.rightMargin: 5
color: "#212121"
}
Item {
width: 48; height: 48
id: folderImage
width: itemHeight
height: itemHeight
Image {
id: folderPicture
source: "qrc:/folder.png"
anchors.centerIn: parent
width: itemHeight * 0.9
height: itemHeight * 0.9
anchors.left: parent.left
anchors.margins: 5
visible: folders.isFolder(index)
}
}
Text {
id: nameText
anchors.fill: parent; verticalAlignment: Text.AlignVCenter
anchors.fill: parent;
verticalAlignment: Text.AlignVCenter
text: fileName
anchors.leftMargin: 54
font.pixelSize: 32
anchors.leftMargin: itemHeight + 10
color: (wrapper.ListView.isCurrentItem && root.showFocusHighlight) ? palette.highlightedText : textColor
elide: Text.ElideRight
}
@@ -142,7 +150,7 @@ Rectangle {
root.showFocusHighlight = false;
wrapper.ListView.view.currentIndex = index;
}
onClicked: { if (folders == wrapper.ListView.view.model) launch() }
onClicked: { if (folders === wrapper.ListView.view.model) launch() }
}
states: [
@@ -160,17 +168,12 @@ Rectangle {
id: view1
anchors.top: titleBar.bottom
anchors.bottom: cancelButton.top
x: 0
width: parent.width
model: folders1
delegate: folderDelegate
highlight: Rectangle {
color: palette.highlight
color: "#212121"
visible: root.showFocusHighlight && view1.count != 0
gradient: Gradient {
GradientStop { id: t1; position: 0.0; color: palette.highlight }
GradientStop { id: t2; position: 1.0; color: Qt.lighter(palette.highlight) }
}
width: view1.currentItem == null ? 0 : view1.currentItem.width
}
highlightMoveVelocity: 1000
@@ -215,12 +218,8 @@ Rectangle {
model: folders2
delegate: folderDelegate
highlight: Rectangle {
color: palette.highlight
color: "#212121"
visible: root.showFocusHighlight && view2.count != 0
gradient: Gradient {
GradientStop { id: t1; position: 0.0; color: palette.highlight }
GradientStop { id: t2; position: 1.0; color: Qt.lighter(palette.highlight) }
}
width: view1.currentItem == null ? 0 : view1.currentItem.width
}
highlightMoveVelocity: 1000
@@ -254,19 +253,29 @@ Rectangle {
}
Rectangle {
id: cancelButton
width: 100
height: titleBar.height - 7
width: parent.width
height: buttonHeight + 10
anchors.bottom: parent.bottom
color: "black"
anchors { bottom: parent.bottom; horizontalCenter: parent.horizontalCenter }
}
Rectangle {
id: cancelButton
width: parent.width
height: buttonHeight
color: "#212121"
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.margins: 5
radius: buttonHeight / 15
Text {
anchors { fill: parent; margins: 4 }
anchors.fill: parent
text: "Cancel"
color: "white"
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
font.pixelSize: 20
}
MouseArea {
@@ -277,55 +286,66 @@ Rectangle {
Keys.onPressed: {
root.keyPressed(event.key);
if (event.key == Qt.Key_Return || event.key == Qt.Key_Select || event.key == Qt.Key_Right) {
if (event.key === Qt.Key_Return || event.key === Qt.Key_Select || event.key === Qt.Key_Right) {
view.currentItem.launch();
event.accepted = true;
} else if (event.key == Qt.Key_Left) {
} else if (event.key === Qt.Key_Left) {
up();
}
}
BorderImage {
source: "qrc:/titlebar.sci";
width: parent.width;
height: 52
y: -7
Rectangle {
id: titleBar
width: parent.width
height: buttonHeight + 10
anchors.top: parent.top
color: "black"
Rectangle {
id: upButton
width: 48
height: titleBar.height - 7
color: "transparent"
Image { anchors.centerIn: parent; source: "qrc:/up.png" }
MouseArea { id: upRegion; anchors.centerIn: parent
width: 56
height: 56
onClicked: up()
}
states: [
State {
name: "pressed"
when: upRegion.pressed
PropertyChanges { target: upButton; color: palette.highlight }
width: parent.width;
height: buttonHeight
color: "#212121"
anchors.margins: 5
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
radius: buttonHeight / 15
Rectangle {
id: upButton
width: buttonHeight
height: buttonHeight
color: "transparent"
Image {
width: itemHeight
height: itemHeight
anchors.centerIn: parent
source: "qrc:/up.png"
}
]
}
MouseArea { id: upRegion; anchors.centerIn: parent
width: buttonHeight
height: buttonHeight
onClicked: up()
}
states: [
State {
name: "pressed"
when: upRegion.pressed
PropertyChanges { target: upButton; color: palette.highlight }
}
]
}
Rectangle {
color: "gray"
x: 48
width: 1
height: 44
}
Text {
anchors.left: upButton.right; anchors.right: parent.right; height: parent.height
anchors.leftMargin: 4; anchors.rightMargin: 4
text: folders.folder
color: "white"
elide: Text.ElideLeft; horizontalAlignment: Text.AlignRight; verticalAlignment: Text.AlignVCenter
font.pixelSize: 32
Text {
anchors.left: upButton.right; anchors.right: parent.right; height: parent.height
anchors.leftMargin: 5; anchors.rightMargin: 5
text: folders.folder
color: "white"
elide: Text.ElideLeft;
horizontalAlignment: Text.AlignLeft;
verticalAlignment: Text.AlignVCenter
}
}
}
@@ -366,14 +386,14 @@ Rectangle {
function keyPressed(key) {
switch (key) {
case Qt.Key_Up:
case Qt.Key_Down:
case Qt.Key_Left:
case Qt.Key_Right:
root.showFocusHighlight = true;
case Qt.Key_Up:
case Qt.Key_Down:
case Qt.Key_Left:
case Qt.Key_Right:
root.showFocusHighlight = true;
break;
default:
// do nothing
default:
// do nothing
break;
}
}

View File

@@ -39,9 +39,9 @@ Rectangle {
property alias buttonHeight: closeButton.height
property string source1
property string source2
property int contentWidth: 250
property int contentWidth: parent.width / 2
property real volume: 0.25
property int margins: 10
property int margins: 5
property QtObject content
signal close
@@ -54,9 +54,12 @@ Rectangle {
right: parent.right
margins: root.margins
}
width: 50
height: 30
width: Math.max(parent.width, parent.height) / 12
height: Math.min(parent.width, parent.height) / 12
z: 2.0
bgColor: "#212121"
bgColorSelected: "#757575"
textColorSelected: "white"
text: "Back"
onClicked: root.close()
}

View File

@@ -59,15 +59,13 @@ Scene {
}
text: content.started ? "Tap the screen to stop content"
: "Tap the screen to start content"
color: "yellow"
font.pixelSize: 20
color: "#e0e0e0"
z: 2.0
}
MouseArea {
anchors.fill: parent
onClicked: {
console.log("[qmlvideo] SceneBasic.onClicked, started = " + content.started)
if (content.started)
content.stop()
else

View File

@@ -86,8 +86,7 @@ Scene {
margins: 20
}
text: "Tap on the content to toggle full-screen mode"
color: "yellow"
font.pixelSize: 20
color: "#e0e0e0"
z: 2.0
}

View File

@@ -91,8 +91,7 @@ Scene {
margins: 20
}
text: "Tap on the content to toggle full-screen mode"
color: "yellow"
font.pixelSize: 20
color: "#e0e0e0"
z: 2.0
}

View File

@@ -65,8 +65,7 @@ Scene {
margins: 20
}
text: content() ? content().started ? "Tap to stop" : "Tap to start" : ""
color: "yellow"
font.pixelSize: 20
color: "#e0e0e0"
}
MouseArea {

View File

@@ -53,7 +53,7 @@ Scene {
y: 0.5 * parent.height
width: content.width
height: content.height
color: "yellow"
color: "#e0e0e0"
opacity: 0.5
SequentialAnimation on x {

View File

@@ -56,7 +56,7 @@ Scene {
bottom: rotateNegativeButton.top
margins: parent.margins
}
width: 90
width: Math.max(parent.width, parent.height) / 10
height: root.buttonHeight
text: "Rotate +" + delta
onClicked: content.rotation = content.rotation + delta
@@ -69,7 +69,7 @@ Scene {
verticalCenter: parent.verticalCenter
margins: parent.margins
}
width: 90
width: Math.max(parent.width, parent.height) / 10
height: root.buttonHeight
text: "Rotate -" + delta
onClicked: content.rotation = content.rotation - delta
@@ -82,7 +82,7 @@ Scene {
verticalCenter: parent.verticalCenter
margins: parent.margins
}
width: 30
width: Math.max(parent.width, parent.height) / 25
height: root.buttonHeight
enabled: false
text: content.rotation % 360

View File

@@ -39,44 +39,64 @@ Rectangle {
property string sceneSource: ""
ListModel {
id: list
ListElement { name: "multi"; source: "SceneMulti.qml" }
ListElement { name: "video"; source: "VideoBasic.qml" }
ListElement { name: "video-drag"; source: "VideoDrag.qml" }
ListElement { name: "video-fillmode"; source: "VideoFillMode.qml" }
ListElement { name: "video-fullscreen"; source: "VideoFullScreen.qml" }
ListElement { name: "video-fullscreen-inverted"; source: "VideoFullScreenInverted.qml" }
ListElement { name: "video-metadata"; source: "VideoMetadata.qml" }
ListElement { name: "video-move"; source: "VideoMove.qml" }
ListElement { name: "video-overlay"; source: "VideoOverlay.qml" }
ListElement { name: "video-playbackrate"; source: "VideoPlaybackRate.qml" }
ListElement { name: "video-resize"; source: "VideoResize.qml" }
ListElement { name: "video-rotate"; source: "VideoRotate.qml" }
ListElement { name: "video-spin"; source: "VideoSpin.qml" }
ListElement { name: "video-seek"; source: "VideoSeek.qml" }
ListElement { name: "camera"; source: "CameraBasic.qml" }
ListElement { name: "camera-drag"; source: "CameraDrag.qml" }
ListElement { name: "camera-fullscreen"; source: "CameraFullScreen.qml" }
ListElement { name: "camera-fullscreen-inverted"; source: "CameraFullScreenInverted.qml" }
ListElement { name: "camera-move"; source: "CameraMove.qml" }
ListElement { name: "camera-overlay"; source: "CameraOverlay.qml" }
ListElement { name: "camera-resize"; source: "CameraResize.qml" }
ListElement { name: "camera-rotate"; source: "CameraRotate.qml" }
ListElement { name: "camera-spin"; source: "CameraSpin.qml" }
id: videolist
ListElement { name: "Multi"; source: "SceneMulti.qml" }
ListElement { name: "Video"; source: "VideoBasic.qml" }
ListElement { name: "Drag"; source: "VideoDrag.qml" }
ListElement { name: "Fillmode"; source: "VideoFillMode.qml" }
ListElement { name: "Fullscreen"; source: "VideoFullScreen.qml" }
ListElement { name: "Fullscreen-inverted"; source: "VideoFullScreenInverted.qml" }
ListElement { name: "Metadata"; source: "VideoMetadata.qml" }
ListElement { name: "Move"; source: "VideoMove.qml" }
ListElement { name: "Overlay"; source: "VideoOverlay.qml" }
ListElement { name: "Playback Rate"; source: "VideoPlaybackRate.qml" }
ListElement { name: "Resize"; source: "VideoResize.qml" }
ListElement { name: "Rotate"; source: "VideoRotate.qml" }
ListElement { name: "Spin"; source: "VideoSpin.qml" }
ListElement { name: "Seek"; source: "VideoSeek.qml" }
}
ListModel {
id: cameralist
ListElement { name: "Camera"; source: "CameraBasic.qml" }
ListElement { name: "Drag"; source: "CameraDrag.qml" }
ListElement { name: "Fullscreen"; source: "CameraFullScreen.qml" }
ListElement { name: "Fullscreen-inverted"; source: "CameraFullScreenInverted.qml" }
ListElement { name: "Move"; source: "CameraMove.qml" }
ListElement { name: "Overlay"; source: "CameraOverlay.qml" }
ListElement { name: "Resize"; source: "CameraResize.qml" }
ListElement { name: "Rotate"; source: "CameraRotate.qml" }
ListElement { name: "Spin"; source: "CameraSpin.qml" }
}
Component {
id: delegate
id: leftDelegate
Item {
id: delegateItem
width: root.width
height: itemHeight
width: root.width / 2
height: 0.8 * itemHeight
Button {
id: selectorItem
anchors.centerIn: parent
width: 0.9 * parent.width
height: 0.8 * itemHeight
anchors.fill: parent
anchors.margins: 5
anchors.rightMargin: 2.5
anchors.bottomMargin: 0
text: name
onClicked: root.sceneSource = source
}
}
}
Component {
id: rightDelegate
Item {
width: root.width / 2
height: 0.8 * itemHeight
Button {
anchors.fill: parent
anchors.margins: 5
anchors.leftMargin: 2.5
anchors.bottomMargin: 0
text: name
onClicked: root.sceneSource = source
}
@@ -85,20 +105,29 @@ Rectangle {
Flickable {
anchors.fill: parent
contentHeight: (itemHeight * list.count) + layout.anchors.topMargin + layout.spacing
contentHeight: (itemHeight * videolist.count) + 10
clip: true
Column {
Row {
id: layout
anchors {
fill: parent
topMargin: 10
topMargin: 5
bottomMargin: 5
}
Repeater {
model: list
delegate: delegate
Column {
Repeater {
model: videolist
delegate: leftDelegate
}
}
Column {
Repeater {
model: cameralist
delegate: rightDelegate
}
}
}
}

View File

@@ -35,7 +35,7 @@ import QtQuick 2.0
Item {
id: seekControl
height: 46
height: Math.min(parent.width, parent.height) / 20
property int duration: 0
property int playPosition: 0
property int seekPosition: 0
@@ -45,8 +45,9 @@ Item {
Rectangle {
id: background
anchors.fill: parent
color: "black"
color: "white"
opacity: 0.3
radius: parent.height / 15
}
Rectangle {
@@ -60,7 +61,6 @@ Item {
Text {
width: 90
anchors { left: parent.left; top: parent.top; bottom: parent.bottom; leftMargin: 10 }
font { family: "Nokia Sans S60"; pixelSize: 24 }
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignVCenter
color: "white"
@@ -71,7 +71,6 @@ Item {
Text {
width: 90
anchors { right: parent.right; top: parent.top; bottom: parent.bottom; rightMargin: 10 }
font { family: "Nokia Sans S60"; pixelSize: 24 }
horizontalAlignment: Text.AlignRight
verticalAlignment: Text.AlignVCenter
color: "white"
@@ -79,35 +78,36 @@ Item {
text: formatTime(duration)
}
Image {
Rectangle {
id: progressHandle
height: 46
width: 10
source: mouseArea.pressed ? "qrc:/images/progress_handle_pressed.svg" : "qrc:/images/progress_handle.svg"
height: parent.height
width: parent.height / 2
color: "white"
opacity: 0.5
anchors.verticalCenter: progressBar.verticalCenter
x: seekControl.duration == 0 ? 0 : seekControl.playPosition / seekControl.duration * 630
x: seekControl.duration == 0 ? 0 : seekControl.playPosition / seekControl.duration * background.width
MouseArea {
id: mouseArea
anchors { horizontalCenter: parent.horizontalCenter; bottom: parent.bottom }
height: 46+16
width: height
height: parent.height
width: parent.height * 2
enabled: seekControl.enabled
drag {
target: progressHandle
axis: Drag.XAxis
minimumX: 0
maximumX: 631
maximumX: background.width
}
onPressed: {
seekControl.seeking = true;
}
onCanceled: {
seekControl.seekPosition = progressHandle.x * seekControl.duration / 630
seekControl.seekPosition = progressHandle.x * seekControl.duration / background.width
seekControl.seeking = false
}
onReleased: {
seekControl.seekPosition = progressHandle.x * seekControl.duration / 630
seekControl.seekPosition = progressHandle.x * seekControl.duration / background.width
seekControl.seeking = false
mouse.accepted = true
}
@@ -120,7 +120,7 @@ Item {
interval: 300
running: seekControl.seeking
onTriggered: {
seekControl.seekPosition = progressHandle.x*seekControl.duration/630
seekControl.seekPosition = progressHandle.x*seekControl.duration / background.width
}
}

View File

@@ -54,16 +54,16 @@ Scene {
verticalCenter: parent.verticalCenter
margins: parent.margins
}
width: 150
width: Math.max(parent.width, parent.height) / 5
height: root.buttonHeight
text: "PreserveAspectFit"
onClicked: {
if (!content.dummy) {
var video = content.contentItem()
if (video.fillMode == VideoOutput.Stretch) {
if (video.fillMode === VideoOutput.Stretch) {
video.fillMode = VideoOutput.PreserveAspectFit
text = "PreserveAspectFit"
} else if (video.fillMode == VideoOutput.PreserveAspectFit) {
} else if (video.fillMode === VideoOutput.PreserveAspectFit) {
video.fillMode = VideoOutput.PreserveAspectCrop
text = "PreserveAspectCrop"
} else {

View File

@@ -56,55 +56,55 @@ Scene {
Column {
anchors.fill: parent
Text {
color: "yellow"
color: "#e0e0e0"
text: "Title:" + content.contentItem().metaData.title
}
Text {
color: "yellow"
color: "#e0e0e0"
text: "Size:" + content.contentItem().metaData.size
}
Text {
color: "yellow"
color: "#e0e0e0"
text: "Resolution:" + content.contentItem().metaData.resolution
}
Text {
color: "yellow"
color: "#e0e0e0"
text: "Media type:" + content.contentItem().metaData.mediaType
}
Text {
color: "yellow"
color: "#e0e0e0"
text: "Video codec:" + content.contentItem().metaData.videoCodec
}
Text {
color: "yellow"
color: "#e0e0e0"
text: "Video bit rate:" + content.contentItem().metaData.videoBitRate
}
Text {
color: "yellow"
color: "#e0e0e0"
text: "Video frame rate:" +content.contentItem().metaData.videoFrameRate
}
Text {
color: "yellow"
color: "#e0e0e0"
text: "Audio codec:" + content.contentItem().metaData.audioCodec
}
Text {
color: "yellow"
color: "#e0e0e0"
text: "Audio bit rate:" + content.contentItem().metaData.audioBitRate
}
Text {
color: "yellow"
color: "#e0e0e0"
text: "Date:" + content.contentItem().metaData.date
}
Text {
color: "yellow"
color: "#e0e0e0"
text: "Description:" + content.contentItem().metaData.description
}
Text {
color: "yellow"
color: "#e0e0e0"
text: "Copyright:" + content.contentItem().metaData.copyright
}
Text {
color: "yellow"
color: "#e0e0e0"
text: "Seekable:" + content.contentItem().metaData.seekable
}
}

View File

@@ -56,12 +56,12 @@ Scene {
bottom: decreaseButton.top
margins: parent.margins
}
width: 90
width: Math.max(parent.width, parent.height) / 10
height: root.buttonHeight
text: "Increase"
onClicked: {
var video = content.contentItem()
video.playbackRate = video.playbackRate + delta
video.playbackRate += delta
}
}
@@ -72,12 +72,12 @@ Scene {
verticalCenter: parent.verticalCenter
margins: parent.margins
}
width: 90
width: Math.max(parent.width, parent.height) / 10
height: root.buttonHeight
text: "Decrease"
onClicked: {
var video = content.contentItem()
video.playbackRate = video.playbackRate - delta
video.playbackRate -= delta
}
}
@@ -88,7 +88,7 @@ Scene {
verticalCenter: parent.verticalCenter
margins: parent.margins
}
width: 50
width: Math.max(parent.width, parent.height) / 25
height: root.buttonHeight
enabled: false
text: Math.round(10 * content.contentItem().playbackRate) / 10

View File

@@ -36,6 +36,7 @@ import QtQuick 2.0
Scene {
id: root
property string contentType: "video"
contentWidth: parent.width
Content {
id: content
@@ -51,13 +52,12 @@ Scene {
anchors {
left: parent.left
right: parent.right
leftMargin: 100
rightMargin: 140
margins: 10
bottom: parent.bottom
}
duration: content.contentItem() ? content.contentItem().duration : 0
playPosition: content.contentItem() ? content.contentItem().position : 0
onSeekPositionChanged: { content.contentItem().seek(seekPosition); }
onSeekPositionChanged: content.contentItem().seek(seekPosition);
}
Component.onCompleted: root.content = content

View File

@@ -35,28 +35,21 @@ import QtQuick 2.0
Rectangle {
id: root
width: 640
height: 360
anchors.fill: parent
color: "black"
property string source1
property string source2
property color bgColor: "#002244"
property color bgColor: "black"
property real volume: 0.25
property bool perfMonitorsLogging: false
property bool perfMonitorsVisible: false
QtObject {
id: d
property int itemHeight: 40
property int itemHeight: root.height > root.width ? root.width / 10 : root.height / 10
property int buttonHeight: 0.8 * itemHeight
property int margins: 10
}
// Create ScreenSaver element via Loader, so this app will still run if the
// SystemInfo module is not available
Loader {
source: "DisableScreenSaver.qml"
property int margins: 5
}
Loader {
@@ -71,7 +64,6 @@ Rectangle {
}
function init() {
console.log("[qmlvideo] performanceLoader.init logging " + root.perfMonitorsLogging + " visible " + root.perfMonitorsVisible)
var enabled = root.perfMonitorsLogging || root.perfMonitorsVisible
source = enabled ? "../performancemonitor/PerformanceItem.qml" : ""
}
@@ -99,6 +91,9 @@ Rectangle {
right: exitButton.left
margins: d.margins
}
bgColor: "#212121"
bgColorSelected: "#757575"
textColorSelected: "white"
height: d.buttonHeight
text: (root.source1 == "") ? "Select file 1" : root.source1
onClicked: fileBrowser1.show()
@@ -112,6 +107,9 @@ Rectangle {
right: exitButton.left
margins: d.margins
}
bgColor: "#212121"
bgColorSelected: "#757575"
textColorSelected: "white"
height: d.buttonHeight
text: (root.source2 == "") ? "Select file 2" : root.source2
onClicked: fileBrowser2.show()
@@ -124,26 +122,58 @@ Rectangle {
right: parent.right
margins: d.margins
}
width: 50
bgColor: "#212121"
bgColorSelected: "#757575"
textColorSelected: "white"
width: parent.width / 10
height: d.buttonHeight
text: "Exit"
onClicked: Qt.quit()
}
Row {
id: modes
anchors.top: openFile2Button.bottom
anchors.margins: 0
anchors.topMargin: 5
Button {
width: root.width / 2
height: 0.8 * d.itemHeight
bgColor: "#212121"
radius: 0
text: "Video Modes"
enabled: false
}
Button {
width: root.width / 2
height: 0.8 * d.itemHeight
bgColor: "#212121"
radius: 0
text: "Camera Modes"
enabled: false
}
}
Rectangle {
id: divider
height: 1
width: parent.width
color: "black"
anchors.top: modes.bottom
}
SceneSelectionPanel {
id: sceneSelectionPanel
itemHeight: d.itemHeight
color: "#004444"
color: "#212121"
anchors {
top: openFile2Button.bottom
top: divider.bottom
left: parent.left
right: parent.right
bottom: parent.bottom
margins: d.margins
}
radius: 10
radius: 0
onSceneSourceChanged: {
console.log("[qmlvideo] main.onSceneSourceChanged source " + sceneSource)
sceneLoader.source = sceneSource
var scene = null
var innerVisible = true
@@ -213,7 +243,9 @@ Rectangle {
ErrorDialog {
id: errorDialog
anchors.fill: parent
anchors.fill: root
dialogWidth: d.itemHeight * 5
dialogHeight: d.itemHeight * 3
enabled: false
}
@@ -230,7 +262,6 @@ Rectangle {
}
function closeScene() {
console.log("[qmlvideo] main.closeScene")
sceneSelectionPanel.sceneSource = ""
}
}

View File

@@ -1,13 +1,8 @@
<RCC>
<qresource prefix="/">
<file alias="leaves.jpg">images/leaves.jpg</file>
<file alias="close.png">images/close.png</file>
<file alias="folder.png">images/folder.png</file>
<file alias="titlebar.png">images/titlebar.png</file>
<file alias="titlebar.sci">images/titlebar.sci</file>
<file alias="up.png">images/up.png</file>
<file alias="progress_handle.svg">images/progress_handle.svg</file>
<file alias="progress_handle_pressed.svg">images/progress_handle_pressed.svg</file>
<file>qml/qmlvideo/Button.qml</file>
<file>qml/qmlvideo/CameraBasic.qml</file>
<file>qml/qmlvideo/CameraDrag.qml</file>
@@ -21,7 +16,6 @@
<file>qml/qmlvideo/CameraRotate.qml</file>
<file>qml/qmlvideo/CameraSpin.qml</file>
<file>qml/qmlvideo/Content.qml</file>
<file>qml/qmlvideo/DisableScreenSaver.qml</file>
<file>qml/qmlvideo/ErrorDialog.qml</file>
<file>qml/qmlvideo/FileBrowser.qml</file>
<file>qml/qmlvideo/main.qml</file>

View File

@@ -32,7 +32,6 @@
****************************************************************************/
#include "filereader.h"
#include "trace.h"
#include <QCoreApplication>
#include <QDir>
@@ -42,7 +41,6 @@
QString FileReader::readFile(const QString &fileName)
{
qtTrace() << "FileReader::readFile" << "fileName" << fileName;
QString content;
QFile file(fileName);
if (file.open(QIODevice::ReadOnly)) {

View File

@@ -84,7 +84,6 @@ Rectangle {
}
onEffectSourceChanged: {
console.log("[qmlvideofx] Content.onEffectSourceChanged " + effectSource)
effectLoader.source = effectSource
effectLoader.item.parent = root
effectLoader.item.targetWidth = root.width
@@ -96,7 +95,6 @@ Rectangle {
}
function init() {
console.log("[qmlvideofx] Content.init")
openImage("qrc:/images/qt-logo.png")
root.effectSource = "EffectPassThrough.qml"
}
@@ -107,7 +105,6 @@ Rectangle {
}
function updateSource() {
console.log("[qmlvideofx] Content.updateSource")
if (contentLoader.item) {
contentLoader.item.parent = root
contentLoader.item.anchors.fill = root
@@ -118,7 +115,6 @@ Rectangle {
}
function openImage(path) {
console.log("[qmlvideofx] Content.openImage \"" + path + "\"")
stop()
contentLoader.source = "ContentImage.qml"
videoFramePaintedConnection.target = null
@@ -127,7 +123,6 @@ Rectangle {
}
function openVideo(path) {
console.log("[qmlvideofx] Content.openVideo \"" + path + "\"")
stop()
contentLoader.source = "ContentVideo.qml"
videoFramePaintedConnection.target = contentLoader.item
@@ -138,7 +133,6 @@ Rectangle {
}
function openCamera() {
console.log("[qmlvideofx] Content.openCamera")
stop()
contentLoader.source = "ContentCamera.qml"
videoFramePaintedConnection.target = contentLoader.item
@@ -146,7 +140,6 @@ Rectangle {
}
function stop() {
console.log("[qmlvideofx] Content.stop")
if (contentLoader.source == "ContentVideo.qml")
contentLoader.item.stop()
theSource.sourceItem = null

View File

@@ -1,43 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the Qt Mobility Components.
**
** $QT_BEGIN_LICENSE:LGPL21$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** $QT_END_LICENSE$
**
****************************************************************************/
import QtQuick 2.0
import QtMultimedia 5.0
VideoOutput {
source: camera
Camera {
id: camera
}
}

View File

@@ -1,43 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the Qt Mobility Components.
**
** $QT_BEGIN_LICENSE:LGPL21$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** $QT_END_LICENSE$
**
****************************************************************************/
import QtQuick 2.0
import QtMobility.systeminfo 1.1
// NOTE: The QtSystemInfo module is not yet part of Qt 5
Item {
ScreenSaver {
screenSaverInhibited: true
}
}

View File

@@ -31,7 +31,8 @@
**
****************************************************************************/
import QtQuick 2.0
import QtQuick 2.1
import QtQuick.Window 2.1
Effect {
id: root
@@ -49,6 +50,7 @@ Effect {
property real posX: -1
property real posY: -1
property real pixDens: Screen.pixelDensity
QtObject {
id: d

View File

@@ -31,7 +31,8 @@
**
****************************************************************************/
import QtQuick 2.0
import QtQuick 2.1
import QtQuick.Window 2.1
Effect {
parameters: ListModel {
@@ -48,6 +49,7 @@ Effect {
// Transform slider values, and bind result to shader uniforms
property real amplitude: parameters.get(0).value * 0.03
property real n: parameters.get(1).value * 7
property real pixDens: Screen.pixelDensity
property real time: 0
NumberAnimation on time { loops: Animation.Infinite; from: 0; to: Math.PI * 2; duration: 600 }

View File

@@ -43,7 +43,7 @@ ListModel {
ListElement { name: "Emboss"; source: "EffectEmboss.qml" }
ListElement { name: "Glow"; source: "EffectGlow.qml" }
ListElement { name: "Isolate"; source: "EffectIsolate.qml" }
//ListElement { name: "Magnify"; source: "EffectMagnify.qml" }
ListElement { name: "Magnify"; source: "EffectMagnify.qml" }
ListElement { name: "Page curl"; source: "EffectPageCurl.qml" }
ListElement { name: "Pixelate"; source: "EffectPixelate.qml" }
ListElement { name: "Posterize"; source: "EffectPosterize.qml" }

View File

@@ -160,7 +160,6 @@ Rectangle {
Loader {
id: performanceLoader
function init() {
console.log("[qmlvideofx] performanceLoader.init logging " + root.perfMonitorsLogging + " visible " + root.perfMonitorsVisible)
var enabled = root.perfMonitorsLogging || root.perfMonitorsVisible
source = enabled ? "../performancemonitor/PerformanceItem.qml" : ""
}
@@ -249,11 +248,6 @@ Rectangle {
height = windowHeight
width = windowWidth
console.log("[qmlvideofx] root.init")
console.log("Height: ", Screen.desktopAvailableHeight)
console.log("Width: ", Screen.desktopAvailableWidth)
console.log("Pixels per mm: ", Math.ceil(Screen.pixelDensity))
console.log("Orientation: ", Screen.orientation)
imageFileBrowser.folder = imagePath
videoFileBrowser.folder = videoPath
content.init()

View File

@@ -3,10 +3,8 @@
<file>images/qt-logo.png</file>
<file>qml/qmlvideofx/Button.qml</file>
<file>qml/qmlvideofx/Content.qml</file>
<file>qml/qmlvideofx/ContentCamera.qml</file>
<file>qml/qmlvideofx/ContentImage.qml</file>
<file>qml/qmlvideofx/ContentVideo.qml</file>
<file>qml/qmlvideofx/DisableScreenSaver.qml</file>
<file>qml/qmlvideofx/Divider.qml</file>
<file>qml/qmlvideofx/Effect.qml</file>
<file>qml/qmlvideofx/EffectBillboard.qml</file>

View File

@@ -50,12 +50,15 @@ uniform float targetWidth;
uniform float targetHeight;
uniform float posX;
uniform float posY;
uniform float pixDens;
void main()
{
vec2 tc = qt_TexCoord0;
vec2 center = vec2(posX, posY);
vec2 xy = gl_FragCoord.xy - center.xy;
xy.x -= (pixDens * 14.0);
xy.y -= (pixDens * 29.0);
float r = sqrt(xy.x * xy.x + xy.y * xy.y);
if (r < radius) {
float h = diffractionIndex * 0.5 * radius;

View File

@@ -55,12 +55,13 @@ const int ITER = 7;
const float RATE = 0.1;
uniform float amplitude;
uniform float n;
uniform float pixDens;
void main()
{
vec2 uv = qt_TexCoord0.xy;
vec2 tc = uv;
vec2 p = vec2(-1.0 + 2.0 * gl_FragCoord.x / targetWidth, -(-1.0 + 2.0 * gl_FragCoord.y / targetHeight));
vec2 p = vec2(-1.0 + 2.0 * (gl_FragCoord.x - (pixDens * 14.0)) / targetWidth, -(-1.0 + 2.0 * (gl_FragCoord.y - (pixDens * 29.0)) / targetHeight));
float diffx = 0.0;
float diffy = 0.0;
vec4 col;

View File

@@ -125,7 +125,6 @@ void FrequencyMonitorPrivate::calculateAverageFrequency()
void FrequencyMonitorPrivate::stalled()
{
if (m_instantaneousFrequency) {
qtVerboseTrace() << "FrequencyMonitor::stalled";
m_instantaneousFrequency = 0;
emit q_ptr->instantaneousFrequencyChanged(m_instantaneousFrequency);
emit q_ptr->frequencyChanged();
@@ -136,7 +135,6 @@ FrequencyMonitor::FrequencyMonitor(QObject *parent)
: QObject(parent)
{
d_ptr = new FrequencyMonitorPrivate(this);
qtTrace() << "FrequencyMonitor::FrequencyMonitor";
}
FrequencyMonitor::~FrequencyMonitor()

View File

@@ -46,6 +46,7 @@
#include <QMediaService>
#include <QMediaRecorder>
#include <QCameraViewfinder>
#include <QCameraInfo>
#include <QMediaMetaData>
#include <QMessageBox>
@@ -53,6 +54,8 @@
#include <QtWidgets>
Q_DECLARE_METATYPE(QCameraInfo)
Camera::Camera(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::Camera),
@@ -65,26 +68,23 @@ Camera::Camera(QWidget *parent) :
ui->setupUi(this);
//Camera devices:
QByteArray cameraDevice;
QActionGroup *videoDevicesGroup = new QActionGroup(this);
videoDevicesGroup->setExclusive(true);
foreach(const QByteArray &deviceName, QCamera::availableDevices()) {
QString description = camera->deviceDescription(deviceName);
QAction *videoDeviceAction = new QAction(description, videoDevicesGroup);
foreach (const QCameraInfo &cameraInfo, QCameraInfo::availableCameras()) {
QAction *videoDeviceAction = new QAction(cameraInfo.description(), videoDevicesGroup);
videoDeviceAction->setCheckable(true);
videoDeviceAction->setData(QVariant(deviceName));
if (cameraDevice.isEmpty()) {
cameraDevice = deviceName;
videoDeviceAction->setData(QVariant::fromValue(cameraInfo));
if (cameraInfo == QCameraInfo::defaultCamera())
videoDeviceAction->setChecked(true);
}
ui->menuDevices->addAction(videoDeviceAction);
}
connect(videoDevicesGroup, SIGNAL(triggered(QAction*)), SLOT(updateCameraDevice(QAction*)));
connect(ui->captureWidget, SIGNAL(currentChanged(int)), SLOT(updateCaptureMode()));
setCamera(cameraDevice);
setCamera(QCameraInfo::defaultCamera());
}
Camera::~Camera()
@@ -94,16 +94,13 @@ Camera::~Camera()
delete camera;
}
void Camera::setCamera(const QByteArray &cameraDevice)
void Camera::setCamera(const QCameraInfo &cameraInfo)
{
delete imageCapture;
delete mediaRecorder;
delete camera;
if (cameraDevice.isEmpty())
camera = new QCamera;
else
camera = new QCamera(cameraDevice);
camera = new QCamera(cameraInfo);
connect(camera, SIGNAL(stateChanged(QCamera::State)), this, SLOT(updateCameraState(QCamera::State)));
connect(camera, SIGNAL(error(QCamera::Error)), this, SLOT(displayCameraError()));
@@ -398,7 +395,7 @@ void Camera::displayCameraError()
void Camera::updateCameraDevice(QAction *action)
{
setCamera(action->data().toByteArray());
setCamera(qvariant_cast<QCameraInfo>(action->data()));
}
void Camera::displayViewfinder()

View File

@@ -60,7 +60,7 @@ public:
~Camera();
private slots:
void setCamera(const QByteArray &cameraDevice);
void setCamera(const QCameraInfo &cameraInfo);
void startCamera();
void stopCamera();

View File

@@ -12,10 +12,6 @@ win32 {
qtCompileTest(evr)
} else:mac {
qtCompileTest(avfoundation)
} else:android:!android-no-sdk {
SDK_ROOT = $$(ANDROID_SDK_ROOT)
isEmpty(SDK_ROOT): SDK_ROOT = $$DEFAULT_ANDROID_SDK_ROOT
!exists($$SDK_ROOT/platforms/android-11/android.jar): error("QtMultimedia for Android requires API level 11")
} else:qnx {
qtCompileTest(mmrenderer)
} else {
@@ -25,6 +21,7 @@ win32 {
qtCompileTest(gstreamer_photography)
qtCompileTest(gstreamer_encodingprofiles)
qtCompileTest(gstreamer_appsrc)
qtCompileTest(linux_v4l)
}
qtCompileTest(resourcepolicy)
qtCompileTest(gpu_vivante)

View File

@@ -100,6 +100,8 @@ config_gstreamer_appsrc {
LIBS_PRIVATE += -lgstapp-0.10
}
config_linux_v4l: DEFINES += USE_V4L
HEADERS += $$PRIVATE_HEADERS
DESTDIR = $$QT.multimedia.libs

View File

@@ -42,8 +42,10 @@
#include <QtCore/qstringlist.h>
#include <qaudioformat.h>
#include <private/qcore_unix_p.h>
#include <linux/videodev2.h>
#ifdef USE_V4L
# include <private/qcore_unix_p.h>
# include <linux/videodev2.h>
#endif
#include "qgstreamervideoinputdevicecontrol_p.h"
@@ -469,6 +471,7 @@ QVector<QGstUtils::CameraInfo> QGstUtils::enumerateCameras(GstElementFactory *fa
}
}
#ifdef USE_V4L
QDir devDir(QStringLiteral("/dev"));
devDir.setFilter(QDir::System);
@@ -516,6 +519,7 @@ QVector<QGstUtils::CameraInfo> QGstUtils::enumerateCameras(GstElementFactory *fa
}
qt_safe_close(fd);
}
#endif // USE_V4L
return devices;
}

View File

@@ -56,7 +56,6 @@ QVideoSurfaceGstDelegate::QVideoSurfaceGstDelegate(
: m_surface(surface)
, m_pool(0)
, m_renderReturn(GST_FLOW_ERROR)
, m_lastPrerolledBuffer(0)
, m_bytesPerLine(0)
, m_startCanceled(false)
{
@@ -74,7 +73,6 @@ QVideoSurfaceGstDelegate::QVideoSurfaceGstDelegate(
QVideoSurfaceGstDelegate::~QVideoSurfaceGstDelegate()
{
setLastPrerolledBuffer(0);
}
QList<QVideoFrame::PixelFormat> QVideoSurfaceGstDelegate::supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType) const
@@ -209,23 +207,6 @@ GstFlowReturn QVideoSurfaceGstDelegate::render(GstBuffer *buffer)
return m_renderReturn;
}
void QVideoSurfaceGstDelegate::setLastPrerolledBuffer(GstBuffer *prerolledBuffer)
{
// discard previously stored buffer
if (m_lastPrerolledBuffer) {
gst_buffer_unref(m_lastPrerolledBuffer);
m_lastPrerolledBuffer = 0;
}
if (!prerolledBuffer)
return;
// store a reference to the buffer
Q_ASSERT(!m_lastPrerolledBuffer);
m_lastPrerolledBuffer = prerolledBuffer;
gst_buffer_ref(m_lastPrerolledBuffer);
}
void QVideoSurfaceGstDelegate::queuedStart()
{
if (!m_startCanceled) {
@@ -397,8 +378,6 @@ QVideoSurfaceGstSink *QVideoSurfaceGstSink::createSink(QAbstractVideoSurface *su
sink->delegate = new QVideoSurfaceGstDelegate(surface);
g_signal_connect(G_OBJECT(sink), "notify::show-preroll-frame", G_CALLBACK(handleShowPrerollChange), sink);
return sink;
}
@@ -434,16 +413,15 @@ void QVideoSurfaceGstSink::class_init(gpointer g_class, gpointer class_data)
sink_parent_class = reinterpret_cast<GstVideoSinkClass *>(g_type_class_peek_parent(g_class));
GstVideoSinkClass *video_sink_class = reinterpret_cast<GstVideoSinkClass *>(g_class);
video_sink_class->show_frame = QVideoSurfaceGstSink::show_frame;
GstBaseSinkClass *base_sink_class = reinterpret_cast<GstBaseSinkClass *>(g_class);
base_sink_class->get_caps = QVideoSurfaceGstSink::get_caps;
base_sink_class->set_caps = QVideoSurfaceGstSink::set_caps;
base_sink_class->buffer_alloc = QVideoSurfaceGstSink::buffer_alloc;
base_sink_class->start = QVideoSurfaceGstSink::start;
base_sink_class->stop = QVideoSurfaceGstSink::stop;
// base_sink_class->unlock = QVideoSurfaceGstSink::unlock; // Not implemented.
base_sink_class->event = QVideoSurfaceGstSink::event;
base_sink_class->preroll = QVideoSurfaceGstSink::preroll;
base_sink_class->render = QVideoSurfaceGstSink::render;
GstElementClass *element_class = reinterpret_cast<GstElementClass *>(g_class);
element_class->change_state = QVideoSurfaceGstSink::change_state;
@@ -709,27 +687,6 @@ void QVideoSurfaceGstSink::setFrameTimeStamps(QVideoFrame *frame, GstBuffer *buf
}
}
void QVideoSurfaceGstSink::handleShowPrerollChange(GObject *o, GParamSpec *p, gpointer d)
{
Q_UNUSED(o);
Q_UNUSED(p);
QVideoSurfaceGstSink *sink = reinterpret_cast<QVideoSurfaceGstSink *>(d);
gboolean value = true; // "show-preroll-frame" property is true by default
g_object_get(G_OBJECT(sink), "show-preroll-frame", &value, NULL);
GstBuffer *buffer = sink->delegate->lastPrerolledBuffer();
// Render the stored prerolled buffer if requested.
// e.g. player is in stopped mode, then seek operation is requested,
// surface now stores a prerolled frame, but doesn't display it until
// "show-preroll-frame" property is set to "true"
// when switching to pause or playing state.
if (value && buffer) {
sink->delegate->render(buffer);
sink->delegate->setLastPrerolledBuffer(0);
}
}
GstFlowReturn QVideoSurfaceGstSink::buffer_alloc(
GstBaseSink *base, guint64 offset, guint size, GstCaps *caps, GstBuffer **buffer)
{
@@ -842,44 +799,9 @@ gboolean QVideoSurfaceGstSink::stop(GstBaseSink *base)
return TRUE;
}
gboolean QVideoSurfaceGstSink::unlock(GstBaseSink *base)
{
Q_UNUSED(base);
return TRUE;
}
gboolean QVideoSurfaceGstSink::event(GstBaseSink *base, GstEvent *event)
{
// discard prerolled frame
if (event->type == GST_EVENT_FLUSH_START) {
VO_SINK(base);
sink->delegate->setLastPrerolledBuffer(0);
}
return TRUE;
}
GstFlowReturn QVideoSurfaceGstSink::preroll(GstBaseSink *base, GstBuffer *buffer)
GstFlowReturn QVideoSurfaceGstSink::show_frame(GstVideoSink *base, GstBuffer *buffer)
{
VO_SINK(base);
gboolean value = true; // "show-preroll-frame" property is true by default
g_object_get(G_OBJECT(base), "show-preroll-frame", &value, NULL);
if (value) {
sink->delegate->setLastPrerolledBuffer(0); // discard prerolled buffer
return sink->delegate->render(buffer); // display frame
}
// otherwise keep a reference to the buffer to display it later
sink->delegate->setLastPrerolledBuffer(buffer);
return GST_FLOW_OK;
}
GstFlowReturn QVideoSurfaceGstSink::render(GstBaseSink *base, GstBuffer *buffer)
{
VO_SINK(base);
sink->delegate->setLastPrerolledBuffer(0); // discard prerolled buffer
return sink->delegate->render(buffer);
}

View File

@@ -281,6 +281,9 @@ void QDeclarativeRadioData::_q_availabilityChanged(QMultimedia::AvailabilityStat
void QDeclarativeRadioData::connectSignals()
{
if (!m_radioData)
return;
connect(m_radioData, SIGNAL(programTypeChanged(QRadioData::ProgramType)), this,
SLOT(_q_programTypeChanged(QRadioData::ProgramType)));

View File

@@ -45,9 +45,6 @@
#include "qsoundeffect_qaudio_p.h"
#include <QtCore/qcoreapplication.h>
#include <QtCore/qthread.h>
#include <QtCore/qmutex.h>
#include <QtCore/qwaitcondition.h>
#include <QtCore/qiodevice.h>
//#include <QDebug>

View File

@@ -84,9 +84,6 @@ public:
GstFlowReturn render(GstBuffer *buffer);
GstBuffer *lastPrerolledBuffer() const { return m_lastPrerolledBuffer; }
void setLastPrerolledBuffer(GstBuffer *lastPrerolledBuffer); // set prerolledBuffer to 0 to discard prerolled buffer
private slots:
void queuedStart();
void queuedStop();
@@ -108,8 +105,6 @@ private:
QVideoSurfaceFormat m_format;
QVideoFrame m_frame;
GstFlowReturn m_renderReturn;
// this pointer is not 0 when there is a prerolled buffer waiting to be displayed
GstBuffer *m_lastPrerolledBuffer;
int m_bytesPerLine;
bool m_started;
bool m_startCanceled;
@@ -126,8 +121,6 @@ public:
QAbstractVideoBuffer::HandleType handleType = QAbstractVideoBuffer::NoHandle);
static void setFrameTimeStamps(QVideoFrame *frame, GstBuffer *buffer);
static void handleShowPrerollChange(GObject *o, GParamSpec *p, gpointer d);
private:
static GType get_type();
static void class_init(gpointer g_class, gpointer class_data);
@@ -147,11 +140,7 @@ private:
static gboolean start(GstBaseSink *sink);
static gboolean stop(GstBaseSink *sink);
static gboolean unlock(GstBaseSink *sink);
static gboolean event(GstBaseSink *sink, GstEvent *event);
static GstFlowReturn preroll(GstBaseSink *sink, GstBuffer *buffer);
static GstFlowReturn render(GstBaseSink *sink, GstBuffer *buffer);
static GstFlowReturn show_frame(GstVideoSink *sink, GstBuffer *buffer);
private:
QVideoSurfaceGstDelegate *delegate;

View File

@@ -51,6 +51,30 @@ public:
virtual void parseLine(int lineIndex, const QString& line, const QUrl& root) = 0;
protected:
QUrl expandToFullPath(const QUrl &root, const QString &line)
{
// On Linux, backslashes are not converted to forward slashes :/
if (line.startsWith(QLatin1String("//")) || line.startsWith(QLatin1String("\\\\"))) {
// Network share paths are not resolved
return QUrl::fromLocalFile(line);
}
QUrl url(line);
if (url.scheme().isEmpty()) {
// Resolve it relative to root
if (root.isLocalFile())
return root.resolved(QUrl::fromLocalFile(line));
else
return root.resolved(url);
} else if (url.scheme().length() == 1) {
// Assume it's a drive letter for a Windows path
url = QUrl::fromLocalFile(line);
}
return url;
}
Q_SIGNALS:
void newItem(const QVariant& content);
void finished();
@@ -138,29 +162,6 @@ public:
return -1;
}
QUrl expandToFullPath(const QUrl& root, const QString& line)
{
// On Linux, backslashes are not converted to forward slashes :/
if (line.startsWith(QLatin1String("//")) || line.startsWith(QLatin1String("\\\\"))) {
// Network share paths are not resolved
return QUrl::fromLocalFile(line);
}
QUrl url(line);
if (url.scheme().isEmpty()) {
// Resolve it relative to root
if (root.isLocalFile())
return root.resolved(QUrl::fromLocalFile(line));
else
return root.resolved(url);
} else if (url.scheme().length() == 1) {
// Assume it's a drive letter for a Windows path
url = QUrl::fromLocalFile(line);
}
return url;
}
private:
bool m_extendedFormat;
QVariantMap m_extraInfo;
@@ -172,27 +173,9 @@ class PLSParser : public ParserBase
public:
PLSParser(QObject *parent)
: ParserBase(parent)
, m_state(Header)
, m_count(0)
, m_readFlags(0)
{
}
enum ReadFlags
{
FileRead = 0x1,
TitleRead = 0x2,
LengthRead = 0x4,
All = FileRead | TitleRead | LengthRead
};
enum State
{
Header,
Track,
Footer
};
/*
*
The format is essentially that of an INI file structured as follows:
@@ -231,89 +214,25 @@ NumberOfEntries=2
Version=2
*/
inline bool containsFlag(const ReadFlags& flag)
void parseLine(int, const QString &line, const QUrl &root)
{
return (m_readFlags & int(flag)) == flag;
// We ignore everything but 'File' entries, since that's the only thing we care about.
if (!line.startsWith(QLatin1String("File")))
return;
QString value = getValue(line);
if (value.isEmpty())
return;
emit newItem(expandToFullPath(root, value));
}
inline void setFlag(const ReadFlags& flag)
{
m_readFlags |= int(flag);
}
void parseLine(int lineIndex, const QString& line, const QUrl&)
{
switch (m_state) {
case Header:
if (line == QLatin1String("[playlist]")) {
m_state = Track;
setCount(1);
}
break;
case Track:
if (!containsFlag(FileRead) && line.startsWith(m_fileName)) {
m_item[QLatin1String("url")] = getValue(lineIndex, line);
setFlag(FileRead);
} else if (!containsFlag(TitleRead) && line.startsWith(m_titleName)) {
m_item[QMediaMetaData::Title] = getValue(lineIndex, line);
setFlag(TitleRead);
} else if (!containsFlag(LengthRead) && line.startsWith(m_lengthName)) {
//convert from seconds to miliseconds
int length = getValue(lineIndex, line).toInt();
if (length > 0)
m_item[QMediaMetaData::Duration] = length * 1000;
setFlag(LengthRead);
} else if (line.startsWith(QLatin1String("NumberOfEntries"))) {
m_state = Footer;
int entries = getValue(lineIndex, line).toInt();
int count = m_readFlags == 0 ? (m_count - 1) : m_count;
if (entries != count) {
emit error(QPlaylistFileParser::FormatError, tr("Error parsing playlist: %1, expected count = %2").
arg(line, QString::number(count)));
}
break;
}
if (m_readFlags == int(All)) {
emit newItem(m_item);
setCount(m_count + 1);
}
break;
case Footer:
if (line.startsWith(QLatin1String("Version"))) {
int version = getValue(lineIndex, line).toInt();
if (version != 2)
emit error(QPlaylistFileParser::FormatError, QString(tr("Error parsing playlist at line[%1], expected version = 2")).arg(line));
}
break;
}
}
QString getValue(int lineIndex, const QString& line) {
QString getValue(const QString& line) {
int start = line.indexOf('=');
if (start < 0) {
emit error(QPlaylistFileParser::FormatError, QString(tr("Error parsing playlist at line[%1]:%2")).arg(QString::number(lineIndex), line));
if (start < 0)
return QString();
}
return line.midRef(start + 1).trimmed().toString();
}
void setCount(int count) {
m_count = count;
m_fileName = QStringLiteral("File%1").arg(count);
m_titleName = QStringLiteral("Title%1").arg(count);
m_lengthName = QStringLiteral("Length%1").arg(count);
m_item.clear();
m_readFlags = 0;
}
private:
State m_state;
int m_count;
QString m_titleName;
QString m_fileName;
QString m_lengthName;
QVariantMap m_item;
int m_readFlags;
};
}

View File

@@ -66,7 +66,7 @@ inline bool QMediaOpenGLHelper::isANGLE()
#else
bool isANGLE = false;
# if defined(Q_OS_WIN) && (defined(QT_OPENGL_ES_2) || defined(QT_OPENGL_DYNAMIC))
# if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) && (defined(QT_OPENGL_ES_2) || defined(QT_OPENGL_DYNAMIC))
if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES) {
// Although unlikely, technically LibGLES could mean a non-ANGLE EGL/GLES2 implementation too.
// Verify that it is indeed ANGLE.
@@ -98,7 +98,7 @@ inline bool QMediaOpenGLHelper::isANGLE()
# endif // QT_OPENGL_ES_2_ANGLE_STATIC
}
# endif // Q_OS_WIN && (QT_OPENGL_ES_2 || QT_OPENGL_DYNAMIC)
# endif // Q_OS_WIN && !Q_OS_WINCE && (QT_OPENGL_ES_2 || QT_OPENGL_DYNAMIC)
return isANGLE;
#endif // Q_OS_WINRT

View File

@@ -760,7 +760,7 @@ qint64 QAlsaAudioInput::elapsedUSecs() const
if (deviceState == QAudio::StoppedState)
return 0;
return clockStamp.elapsed()*1000;
return clockStamp.elapsed() * qint64(1000);
}
void QAlsaAudioInput::reset()

View File

@@ -774,7 +774,7 @@ qint64 QAlsaAudioOutput::elapsedUSecs() const
if (deviceState == QAudio::StoppedState)
return 0;
return clockStamp.elapsed()*1000;
return clockStamp.elapsed() * qint64(1000);
}
void QAlsaAudioOutput::reset()

View File

@@ -83,23 +83,6 @@ public class QtAndroidMediaPlayer
private volatile int mState = State.Uninitialized;
private class ProgressWatcher
implements Runnable
{
@Override
public void run()
{
try {
while ((mState & (State.Started)) != 0) {
onProgressUpdateNative(getCurrentPosition(), mID);
Thread.sleep(1000);
}
} catch (final InterruptedException e) {
// Ignore
}
}
}
/**
* MediaPlayer OnErrorListener
*/
@@ -257,8 +240,6 @@ public class QtAndroidMediaPlayer
try {
mMediaPlayer.start();
setState(State.Started);
Thread progressThread = new Thread(new ProgressWatcher());
progressThread.start();
} catch (final IllegalStateException e) {
Log.d(TAG, "" + e.getMessage());
}
@@ -309,7 +290,6 @@ public class QtAndroidMediaPlayer
try {
mMediaPlayer.seekTo(msec);
onProgressUpdateNative(msec, mID);
} catch (final IllegalStateException e) {
Log.d(TAG, "" + e.getMessage());
}

View File

@@ -37,6 +37,36 @@
QT_BEGIN_NAMESPACE
class StateChangeNotifier
{
public:
StateChangeNotifier(QAndroidMediaPlayerControl *mp)
: mControl(mp)
, mPreviousState(mp->state())
, mPreviousMediaStatus(mp->mediaStatus())
{
++mControl->mActiveStateChangeNotifiers;
}
~StateChangeNotifier()
{
if (--mControl->mActiveStateChangeNotifiers)
return;
if (mPreviousState != mControl->state())
Q_EMIT mControl->stateChanged(mControl->state());
if (mPreviousMediaStatus != mControl->mediaStatus())
Q_EMIT mControl->mediaStatusChanged(mControl->mediaStatus());
}
private:
QAndroidMediaPlayerControl *mControl;
QMediaPlayer::State mPreviousState;
QMediaPlayer::MediaStatus mPreviousMediaStatus;
};
QAndroidMediaPlayerControl::QAndroidMediaPlayerControl(QObject *parent)
: QMediaPlayerControl(parent),
mMediaPlayer(new AndroidMediaPlayer),
@@ -55,7 +85,9 @@ QAndroidMediaPlayerControl::QAndroidMediaPlayerControl(QObject *parent)
mPendingPosition(-1),
mPendingSetMedia(false),
mPendingVolume(-1),
mPendingMute(-1)
mPendingMute(-1),
mReloadingMedia(false),
mActiveStateChangeNotifiers(0)
{
connect(mMediaPlayer,SIGNAL(bufferingChanged(qint32)),
this,SLOT(onBufferingChanged(qint32)));
@@ -107,17 +139,14 @@ qint64 QAndroidMediaPlayerControl::position() const
if (mCurrentMediaStatus == QMediaPlayer::EndOfMedia)
return duration();
if ((mState & (AndroidMediaPlayer::Idle
| AndroidMediaPlayer::Initialized
| AndroidMediaPlayer::Prepared
if ((mState & (AndroidMediaPlayer::Prepared
| AndroidMediaPlayer::Started
| AndroidMediaPlayer::Paused
| AndroidMediaPlayer::Stopped
| AndroidMediaPlayer::PlaybackCompleted)) == 0) {
return (mPendingPosition == -1) ? 0 : mPendingPosition;
| AndroidMediaPlayer::PlaybackCompleted))) {
return mMediaPlayer->getCurrentPosition();
}
return (mCurrentState == QMediaPlayer::StoppedState) ? 0 : mMediaPlayer->getCurrentPosition();
return (mPendingPosition == -1) ? 0 : mPendingPosition;
}
void QAndroidMediaPlayerControl::setPosition(qint64 position)
@@ -127,24 +156,25 @@ void QAndroidMediaPlayerControl::setPosition(qint64 position)
const int seekPosition = (position > INT_MAX) ? INT_MAX : position;
if ((mState & (AndroidMediaPlayer::Prepared
| AndroidMediaPlayer::Started
| AndroidMediaPlayer::Paused
| AndroidMediaPlayer::PlaybackCompleted)) == 0) {
if (mPendingPosition != seekPosition) {
mPendingPosition = seekPosition;
Q_EMIT positionChanged(seekPosition);
}
if (seekPosition == this->position())
return;
}
StateChangeNotifier notifier(this);
if (mCurrentMediaStatus == QMediaPlayer::EndOfMedia)
setMediaStatus(QMediaPlayer::LoadedMedia);
mMediaPlayer->seekTo(seekPosition);
if ((mState & (AndroidMediaPlayer::Prepared
| AndroidMediaPlayer::Started
| AndroidMediaPlayer::Paused
| AndroidMediaPlayer::PlaybackCompleted)) == 0) {
mPendingPosition = seekPosition;
} else {
mMediaPlayer->seekTo(seekPosition);
if (mPendingPosition != -1) {
mPendingPosition = -1;
if (mPendingPosition != -1) {
mPendingPosition = -1;
}
}
Q_EMIT positionChanged(seekPosition);
@@ -275,9 +305,11 @@ const QIODevice *QAndroidMediaPlayerControl::mediaStream() const
void QAndroidMediaPlayerControl::setMedia(const QMediaContent &mediaContent,
QIODevice *stream)
{
const bool reloading = (mMediaContent == mediaContent);
StateChangeNotifier notifier(this);
if (!reloading) {
mReloadingMedia = (mMediaContent == mediaContent);
if (!mReloadingMedia) {
mMediaContent = mediaContent;
mMediaStream = stream;
}
@@ -286,41 +318,45 @@ void QAndroidMediaPlayerControl::setMedia(const QMediaContent &mediaContent,
if ((mState & (AndroidMediaPlayer::Idle | AndroidMediaPlayer::Uninitialized)) == 0)
mMediaPlayer->release();
QString mediaPath;
if (mediaContent.isNull()) {
setMediaStatus(QMediaPlayer::NoMedia);
return;
}
if (mVideoOutput && !mVideoOutput->isReady()) {
// if a video output is set but the video texture is not ready, delay loading the media
// since it can cause problems on some hardware
mPendingSetMedia = true;
return;
}
const QUrl url = mediaContent.canonicalUrl();
QString mediaPath;
if (url.scheme() == QLatin1String("qrc")) {
const QString path = url.toString().mid(3);
mTempFile.reset(QTemporaryFile::createNativeFile(path));
if (!mTempFile.isNull())
mediaPath = QStringLiteral("file://") + mTempFile->fileName();
} else {
mediaPath = url.toString();
if (mVideoOutput && !mVideoOutput->isReady()) {
// if a video output is set but the video texture is not ready, delay loading the media
// since it can cause problems on some hardware
mPendingSetMedia = true;
return;
}
const QUrl url = mediaContent.canonicalUrl();
if (url.scheme() == QLatin1String("qrc")) {
const QString path = url.toString().mid(3);
mTempFile.reset(QTemporaryFile::createNativeFile(path));
if (!mTempFile.isNull())
mediaPath = QStringLiteral("file://") + mTempFile->fileName();
} else {
mediaPath = url.toString();
}
if (mVideoSize.isValid() && mVideoOutput)
mVideoOutput->setVideoSize(mVideoSize);
if ((mMediaPlayer->display() == 0) && mVideoOutput)
mMediaPlayer->setDisplay(mVideoOutput->surfaceTexture());
mMediaPlayer->setDataSource(mediaPath);
mMediaPlayer->prepareAsync();
}
if (mVideoSize.isValid() && mVideoOutput)
mVideoOutput->setVideoSize(mVideoSize);
if ((mMediaPlayer->display() == 0) && mVideoOutput)
mMediaPlayer->setDisplay(mVideoOutput->surfaceTexture());
mMediaPlayer->setDataSource(mediaPath);
mMediaPlayer->prepareAsync();
if (!reloading)
if (!mReloadingMedia) {
Q_EMIT mediaChanged(mMediaContent);
Q_EMIT actualMediaLocationChanged(mediaPath);
}
resetBufferingProgress();
mReloadingMedia = false;
}
void QAndroidMediaPlayerControl::setVideoOutput(QObject *videoOutput)
@@ -344,6 +380,8 @@ void QAndroidMediaPlayerControl::setVideoOutput(QObject *videoOutput)
void QAndroidMediaPlayerControl::play()
{
StateChangeNotifier notifier(this);
// We need to prepare the mediaplayer again.
if ((mState & AndroidMediaPlayer::Stopped) && !mMediaContent.isNull()) {
setMedia(mMediaContent, mMediaStream);
@@ -364,6 +402,8 @@ void QAndroidMediaPlayerControl::play()
void QAndroidMediaPlayerControl::pause()
{
StateChangeNotifier notifier(this);
setState(QMediaPlayer::PausedState);
if ((mState & (AndroidMediaPlayer::Started
@@ -378,6 +418,8 @@ void QAndroidMediaPlayerControl::pause()
void QAndroidMediaPlayerControl::stop()
{
StateChangeNotifier notifier(this);
setState(QMediaPlayer::StoppedState);
if ((mState & (AndroidMediaPlayer::Prepared
@@ -395,6 +437,8 @@ void QAndroidMediaPlayerControl::stop()
void QAndroidMediaPlayerControl::onInfo(qint32 what, qint32 extra)
{
StateChangeNotifier notifier(this);
Q_UNUSED(extra);
switch (what) {
case AndroidMediaPlayer::MEDIA_INFO_UNKNOWN:
@@ -426,6 +470,8 @@ void QAndroidMediaPlayerControl::onInfo(qint32 what, qint32 extra)
void QAndroidMediaPlayerControl::onError(qint32 what, qint32 extra)
{
StateChangeNotifier notifier(this);
QString errorString;
QMediaPlayer::Error error = QMediaPlayer::ResourceError;
@@ -478,6 +524,8 @@ void QAndroidMediaPlayerControl::onError(qint32 what, qint32 extra)
void QAndroidMediaPlayerControl::onBufferingChanged(qint32 percent)
{
StateChangeNotifier notifier(this);
mBuffering = percent != 100;
mBufferPercent = percent;
@@ -509,6 +557,8 @@ void QAndroidMediaPlayerControl::onStateChanged(qint32 state)
return;
}
StateChangeNotifier notifier(this);
mState = state;
switch (mState) {
case AndroidMediaPlayer::Idle:
@@ -516,7 +566,8 @@ void QAndroidMediaPlayerControl::onStateChanged(qint32 state)
case AndroidMediaPlayer::Initialized:
break;
case AndroidMediaPlayer::Preparing:
setMediaStatus(QMediaPlayer::LoadingMedia);
if (!mReloadingMedia)
setMediaStatus(QMediaPlayer::LoadingMedia);
break;
case AndroidMediaPlayer::Prepared:
setMediaStatus(QMediaPlayer::LoadedMedia);
@@ -537,6 +588,7 @@ void QAndroidMediaPlayerControl::onStateChanged(qint32 state)
} else {
setMediaStatus(QMediaPlayer::BufferedMedia);
}
Q_EMIT positionChanged(position());
break;
case AndroidMediaPlayer::Paused:
setState(QMediaPlayer::PausedState);
@@ -545,27 +597,32 @@ void QAndroidMediaPlayerControl::onStateChanged(qint32 state)
setState(QMediaPlayer::StoppedState);
setMediaStatus(QMediaPlayer::UnknownMediaStatus);
mMediaPlayer->release();
Q_EMIT positionChanged(0);
break;
case AndroidMediaPlayer::Stopped:
setState(QMediaPlayer::StoppedState);
setMediaStatus(QMediaPlayer::LoadedMedia);
setPosition(0);
Q_EMIT positionChanged(0);
break;
case AndroidMediaPlayer::PlaybackCompleted:
setState(QMediaPlayer::StoppedState);
setPosition(0);
setMediaStatus(QMediaPlayer::EndOfMedia);
break;
case AndroidMediaPlayer::Uninitialized:
// reset some properties
resetBufferingProgress();
mPendingPosition = -1;
mPendingSetMedia = false;
mPendingState = -1;
// reset some properties (unless we reload the same media)
if (!mReloadingMedia) {
resetBufferingProgress();
mPendingPosition = -1;
mPendingSetMedia = false;
mPendingState = -1;
setAudioAvailable(false);
setVideoAvailable(false);
setSeekable(true);
Q_EMIT durationChanged(0);
Q_EMIT positionChanged(0);
setAudioAvailable(false);
setVideoAvailable(false);
setSeekable(true);
}
break;
default:
break;
@@ -597,7 +654,6 @@ void QAndroidMediaPlayerControl::setState(QMediaPlayer::State state)
return;
mCurrentState = state;
Q_EMIT stateChanged(mCurrentState);
}
void QAndroidMediaPlayerControl::setMediaStatus(QMediaPlayer::MediaStatus status)
@@ -605,14 +661,13 @@ void QAndroidMediaPlayerControl::setMediaStatus(QMediaPlayer::MediaStatus status
if (mCurrentMediaStatus == status)
return;
mCurrentMediaStatus = status;
if (status == QMediaPlayer::NoMedia || status == QMediaPlayer::InvalidMedia)
Q_EMIT durationChanged(0);
if (status == QMediaPlayer::EndOfMedia)
Q_EMIT durationChanged(duration());
mCurrentMediaStatus = status;
Q_EMIT mediaStatusChanged(mCurrentMediaStatus);
Q_EMIT positionChanged(position());
updateBufferStatus();
}

View File

@@ -72,6 +72,7 @@ public:
Q_SIGNALS:
void metaDataUpdated();
void actualMediaLocationChanged(const QString &url);
public Q_SLOTS:
void setPosition(qint64 position) Q_DECL_OVERRIDE;
@@ -110,7 +111,9 @@ private:
bool mPendingSetMedia;
int mPendingVolume;
int mPendingMute;
bool mReloadingMedia;
QScopedPointer<QTemporaryFile> mTempFile;
int mActiveStateChangeNotifiers;
void setState(QMediaPlayer::State state);
void setMediaStatus(QMediaPlayer::MediaStatus status);
@@ -121,6 +124,8 @@ private:
void resetBufferingProgress();
void flushPendingStates();
void updateBufferStatus();
friend class StateChangeNotifier;
};
QT_END_NAMESPACE

View File

@@ -45,8 +45,8 @@ QAndroidMediaService::QAndroidMediaService(QObject *parent)
{
mMediaControl = new QAndroidMediaPlayerControl;
mMetadataControl = new QAndroidMetaDataReaderControl;
connect(mMediaControl, SIGNAL(mediaChanged(QMediaContent)),
mMetadataControl, SLOT(onMediaChanged(QMediaContent)));
connect(mMediaControl, SIGNAL(actualMediaLocationChanged(QString)),
mMetadataControl, SLOT(onMediaChanged(QString)));
connect(mMediaControl, SIGNAL(metaDataUpdated()),
mMetadataControl, SLOT(onUpdateMetaData()));
}

View File

@@ -93,18 +93,18 @@ QStringList QAndroidMetaDataReaderControl::availableMetaData() const
return m_metadata.keys();
}
void QAndroidMetaDataReaderControl::onMediaChanged(const QMediaContent &media)
void QAndroidMetaDataReaderControl::onMediaChanged(const QString &url)
{
if (!m_retriever)
return;
m_mediaContent = media;
m_mediaLocation = url;
updateData();
}
void QAndroidMetaDataReaderControl::onUpdateMetaData()
{
if (!m_retriever || m_mediaContent.isNull())
if (!m_retriever || m_mediaLocation.isEmpty())
return;
updateData();
@@ -114,8 +114,8 @@ void QAndroidMetaDataReaderControl::updateData()
{
m_metadata.clear();
if (!m_mediaContent.isNull()) {
if (m_retriever->setDataSource(m_mediaContent.canonicalUrl())) {
if (!m_mediaLocation.isEmpty()) {
if (m_retriever->setDataSource(m_mediaLocation)) {
QString mimeType = m_retriever->extractMetadata(AndroidMediaMetadataRetriever::MimeType);
if (!mimeType.isNull())
m_metadata.insert(QMediaMetaData::MediaType, mimeType);

View File

@@ -54,13 +54,13 @@ public:
QStringList availableMetaData() const Q_DECL_OVERRIDE;
public Q_SLOTS:
void onMediaChanged(const QMediaContent &media);
void onMediaChanged(const QString &url);
void onUpdateMetaData();
private:
void updateData();
QMediaContent m_mediaContent;
QString m_mediaLocation;
bool m_available;
QVariantMap m_metadata;

View File

@@ -35,9 +35,24 @@
#include <QtCore/private/qjnihelpers_p.h>
#include <QtCore/private/qjni_p.h>
#include <QtCore/QUrl>
#include <qdebug.h>
QT_BEGIN_NAMESPACE
static bool exceptionCheckAndClear(JNIEnv *env)
{
if (Q_UNLIKELY(env->ExceptionCheck())) {
#ifdef QT_DEBUG
env->ExceptionDescribe();
#endif // QT_DEBUG
env->ExceptionClear();
return true;
}
return false;
}
AndroidMediaMetadataRetriever::AndroidMediaMetadataRetriever()
{
m_metadataRetriever = QJNIObjectPrivate("android/media/MediaMetadataRetriever");
@@ -68,55 +83,105 @@ void AndroidMediaMetadataRetriever::release()
m_metadataRetriever.callMethod<void>("release");
}
bool AndroidMediaMetadataRetriever::setDataSource(const QUrl &url)
bool AndroidMediaMetadataRetriever::setDataSource(const QString &urlString)
{
if (!m_metadataRetriever.isValid())
return false;
QJNIEnvironmentPrivate env;
QUrl url(urlString);
bool loaded = false;
if (url.isLocalFile()) { // also includes qrc files (copied to a temp file)
QJNIObjectPrivate string = QJNIObjectPrivate::fromString(url.path());
QJNIObjectPrivate fileInputStream("java/io/FileInputStream",
"(Ljava/lang/String;)V",
string.object());
QJNIObjectPrivate string = QJNIObjectPrivate::fromString(url.toString());
if (exceptionCheckAndClear(env))
return false;
QJNIObjectPrivate uri = m_metadataRetriever.callStaticObjectMethod("android/net/Uri",
"parse",
"(Ljava/lang/String;)Landroid/net/Uri;",
string.object());
if (env->ExceptionCheck()) {
env->ExceptionClear();
QJNIObjectPrivate fd = fileInputStream.callObjectMethod("getFD",
"()Ljava/io/FileDescriptor;");
if (exceptionCheckAndClear(env)) {
fileInputStream.callMethod<void>("close");
exceptionCheckAndClear(env);
return false;
}
m_metadataRetriever.callMethod<void>("setDataSource",
"(Ljava/io/FileDescriptor;)V",
fd.object());
bool ok = !exceptionCheckAndClear(env);
fileInputStream.callMethod<void>("close");
exceptionCheckAndClear(env);
if (!ok)
return false;
} else if (url.scheme() == QLatin1String("assets")) {
QJNIObjectPrivate string = QJNIObjectPrivate::fromString(url.path().mid(1)); // remove first '/'
QJNIObjectPrivate activity(QtAndroidPrivate::activity());
QJNIObjectPrivate assetManager = activity.callObjectMethod("getAssets",
"()Landroid/content/res/AssetManager;");
QJNIObjectPrivate assetFd = assetManager.callObjectMethod("openFd",
"(Ljava/lang/String;)Landroid/content/res/AssetFileDescriptor;",
string.object());
if (exceptionCheckAndClear(env))
return false;
QJNIObjectPrivate fd = assetFd.callObjectMethod("getFileDescriptor",
"()Ljava/io/FileDescriptor;");
if (exceptionCheckAndClear(env)) {
assetFd.callMethod<void>("close");
exceptionCheckAndClear(env);
return false;
}
m_metadataRetriever.callMethod<void>("setDataSource",
"(Ljava/io/FileDescriptor;JJ)V",
fd.object(),
assetFd.callMethod<jlong>("getStartOffset"),
assetFd.callMethod<jlong>("getLength"));
bool ok = !exceptionCheckAndClear(env);
assetFd.callMethod<void>("close");
exceptionCheckAndClear(env);
if (!ok)
return false;
} else if (QtAndroidPrivate::androidSdkVersion() >= 14) {
// On API levels >= 14, only setDataSource(String, Map<String, String>) accepts remote media
QJNIObjectPrivate string = QJNIObjectPrivate::fromString(urlString);
QJNIObjectPrivate hash("java/util/HashMap");
m_metadataRetriever.callMethod<void>("setDataSource",
"(Ljava/lang/String;Ljava/util/Map;)V",
string.object(),
hash.object());
if (exceptionCheckAndClear(env))
return false;
} else {
// While on API levels < 14, only setDataSource(Context, Uri) is available and works for
// remote media...
QJNIObjectPrivate string = QJNIObjectPrivate::fromString(urlString);
QJNIObjectPrivate uri = m_metadataRetriever.callStaticObjectMethod("android/net/Uri",
"parse",
"(Ljava/lang/String;)Landroid/net/Uri;",
string.object());
if (exceptionCheckAndClear(env))
return false;
m_metadataRetriever.callMethod<void>("setDataSource",
"(Landroid/content/Context;Landroid/net/Uri;)V",
QtAndroidPrivate::activity(),
uri.object());
if (env->ExceptionCheck())
env->ExceptionClear();
else
loaded = true;
if (exceptionCheckAndClear(env))
return false;
}
return loaded;
}
bool AndroidMediaMetadataRetriever::setDataSource(const QString &path)
{
if (!m_metadataRetriever.isValid())
return false;
QJNIEnvironmentPrivate env;
bool loaded = false;
m_metadataRetriever.callMethod<void>("setDataSource",
"(Ljava/lang/String;)V",
QJNIObjectPrivate::fromString(path).object());
if (env->ExceptionCheck())
env->ExceptionClear();
else
loaded = true;
return loaded;
return true;
}
QT_END_NAMESPACE

View File

@@ -35,7 +35,6 @@
#define ANDROIDMEDIAMETADATARETRIEVER_H
#include <QtCore/private/qjni_p.h>
#include <qurl.h>
QT_BEGIN_NAMESPACE
@@ -73,8 +72,7 @@ public:
QString extractMetadata(MetadataKey key);
void release();
bool setDataSource(const QUrl &url);
bool setDataSource(const QString &path);
bool setDataSource(const QString &url);
private:
QJNIObjectPrivate m_metadataRetriever;

View File

@@ -142,9 +142,9 @@ QMediaControl *AVFCameraService::requestControl(const char *name)
void AVFCameraService::releaseControl(QMediaControl *control)
{
if (m_videoOutput == control) {
m_videoOutput = 0;
m_session->setVideoOutput(0);
delete control;
delete m_videoOutput;
m_videoOutput = 0;
}
AVFMediaVideoProbeControl *videoProbe = qobject_cast<AVFMediaVideoProbeControl *>(control);
if (videoProbe) {

View File

@@ -118,14 +118,15 @@ void AVFMediaPlayerService::releaseControl(QMediaControl *control)
#ifdef QT_DEBUG_AVF
qDebug() << Q_FUNC_INFO << control;
#endif
#if defined(Q_OS_OSX)
if (m_videoOutput == control) {
#if defined(Q_OS_OSX)
AVFVideoRendererControl *renderControl = qobject_cast<AVFVideoRendererControl*>(m_videoOutput);
if (renderControl)
renderControl->setSurface(0);
#endif
m_videoOutput = 0;
m_session->setVideoOutput(0);
delete control;
}
#endif
}

View File

@@ -97,8 +97,6 @@ public Q_SLOTS:
void processPositionChange();
void processMediaLoadError();
void processCurrentItemChanged();
Q_SIGNALS:
void positionChanged(qint64 position);
void durationChanged(qint64 duration);
@@ -148,6 +146,9 @@ private:
QByteArray rawData;
};
void setAudioAvailable(bool available);
void setVideoAvailable(bool available);
AVFMediaPlayerService *m_service;
AVFVideoOutput *m_videoOutput;

View File

@@ -70,15 +70,11 @@ static void *AVFMediaPlayerSessionObserverCurrentItemObservationContext = &AVFMe
AVPlayerItem *m_playerItem;
AVPlayerLayer *m_playerLayer;
NSURL *m_URL;
bool m_audioAvailable;
bool m_videoAvailable;
}
@property (readonly, getter=player) AVPlayer* m_player;
@property (readonly, getter=playerItem) AVPlayerItem* m_playerItem;
@property (readonly, getter=playerLayer) AVPlayerLayer* m_playerLayer;
@property (readonly, getter=audioAvailable) bool m_audioAvailable;
@property (readonly, getter=videoAvailable) bool m_videoAvailable;
@property (readonly, getter=session) AVFMediaPlayerSession* m_session;
- (AVFMediaPlayerSessionObserver *) initWithMediaPlayerSession:(AVFMediaPlayerSession *)session;
@@ -96,7 +92,7 @@ static void *AVFMediaPlayerSessionObserverCurrentItemObservationContext = &AVFMe
@implementation AVFMediaPlayerSessionObserver
@synthesize m_player, m_playerItem, m_playerLayer, m_audioAvailable, m_videoAvailable, m_session;
@synthesize m_player, m_playerItem, m_playerLayer, m_session;
- (AVFMediaPlayerSessionObserver *) initWithMediaPlayerSession:(AVFMediaPlayerSession *)session
{
@@ -186,18 +182,6 @@ static void *AVFMediaPlayerSessionObserverCurrentItemObservationContext = &AVFMe
return;
}
m_audioAvailable = false;
m_videoAvailable = false;
//Check each track of asset for audio and video content
NSArray *tracks = [asset tracks];
for (AVAssetTrack *track in tracks) {
if ([track hasMediaCharacteristic:AVMediaCharacteristicAudible])
m_audioAvailable = true;
if ([track hasMediaCharacteristic:AVMediaCharacteristicVisual])
m_videoAvailable = true;
}
//At this point we're ready to set up for playback of the asset.
//Stop observing our prior AVPlayerItem, if we have one.
if (m_playerItem)
@@ -258,18 +242,7 @@ static void *AVFMediaPlayerSessionObserverCurrentItemObservationContext = &AVFMe
m_playerLayer = [AVPlayerLayer playerLayerWithPlayer:m_player];
[m_playerLayer retain];
m_playerLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
//Get the native size of the new item, and reset the bounds of the player layer
AVAsset *asset = m_playerItem.asset;
if (asset) {
NSArray *tracks = [asset tracksWithMediaType:AVMediaTypeVideo];
if ([tracks count]) {
AVAssetTrack *videoTrack = [tracks objectAtIndex:0];
m_playerLayer.anchorPoint = CGPointMake(0.0f, 0.0f);
m_playerLayer.bounds = CGRectMake(0.0f, 0.0f, videoTrack.naturalSize.width, videoTrack.naturalSize.height);
}
}
m_playerLayer.anchorPoint = CGPointMake(0.0f, 0.0f);
}
//Observe the AVPlayer "currentItem" property to find out when any
@@ -322,7 +295,7 @@ static void *AVFMediaPlayerSessionObserverCurrentItemObservationContext = &AVFMe
//AVPlayerItem "status" property value observer.
if (context == AVFMediaPlayerSessionObserverStatusObservationContext)
{
AVPlayerStatus status = [[change objectForKey:NSKeyValueChangeNewKey] integerValue];
AVPlayerStatus status = (AVPlayerStatus)[[change objectForKey:NSKeyValueChangeNewKey] integerValue];
switch (status)
{
//Indicates that the status of the player is not yet known because
@@ -366,24 +339,7 @@ static void *AVFMediaPlayerSessionObserverCurrentItemObservationContext = &AVFMe
{
AVPlayerItem *newPlayerItem = [change objectForKey:NSKeyValueChangeNewKey];
if (m_playerItem != newPlayerItem)
{
m_playerItem = newPlayerItem;
//Get the native size of the new item, and reset the bounds of the player layer
//AVAsset *asset = m_playerItem.asset;
AVAsset *asset = [m_playerItem asset];
if (asset) {
NSArray *tracks = [asset tracksWithMediaType:AVMediaTypeVideo];
if ([tracks count]) {
AVAssetTrack *videoTrack = [tracks objectAtIndex:0];
m_playerLayer.anchorPoint = CGPointMake(0.0f, 0.0f);
m_playerLayer.bounds = CGRectMake(0.0f, 0.0f, videoTrack.naturalSize.width, videoTrack.naturalSize.height);
}
}
}
if (self.session)
QMetaObject::invokeMethod(m_session, "processCurrentItemChanged", Qt::AutoConnection);
}
else
{
@@ -513,6 +469,9 @@ void AVFMediaPlayerSession::setMedia(const QMediaContent &content, QIODevice *st
m_resources = content;
m_mediaStream = stream;
setAudioAvailable(false);
setVideoAvailable(false);
QMediaPlayer::MediaStatus oldMediaStatus = m_mediaStatus;
if (content.isNull() || content.canonicalUrl().isEmpty()) {
@@ -582,14 +541,32 @@ bool AVFMediaPlayerSession::isMuted() const
return m_muted;
}
void AVFMediaPlayerSession::setAudioAvailable(bool available)
{
if (m_audioAvailable == available)
return;
m_audioAvailable = available;
Q_EMIT audioAvailableChanged(available);
}
bool AVFMediaPlayerSession::isAudioAvailable() const
{
return [(AVFMediaPlayerSessionObserver*)m_observer audioAvailable];
return m_audioAvailable;
}
void AVFMediaPlayerSession::setVideoAvailable(bool available)
{
if (m_videoAvailable == available)
return;
m_videoAvailable = available;
Q_EMIT videoAvailableChanged(available);
}
bool AVFMediaPlayerSession::isVideoAvailable() const
{
return [(AVFMediaPlayerSessionObserver*)m_observer videoAvailable];
return m_videoAvailable;
}
bool AVFMediaPlayerSession::isSeekable() const
@@ -802,16 +779,41 @@ void AVFMediaPlayerSession::processLoadStateChange()
bool isPlaying = (m_state != QMediaPlayer::StoppedState);
if (currentStatus == AVPlayerStatusReadyToPlay) {
AVPlayerItem *playerItem = [(AVFMediaPlayerSessionObserver*)m_observer playerItem];
if (playerItem) {
// Check each track for audio and video content
AVAssetTrack *videoTrack = nil;
NSArray *tracks = playerItem.tracks;
for (AVPlayerItemTrack *track in tracks) {
AVAssetTrack *assetTrack = track.assetTrack;
if (assetTrack) {
if ([assetTrack.mediaType isEqualToString:AVMediaTypeAudio])
setAudioAvailable(true);
if ([assetTrack.mediaType isEqualToString:AVMediaTypeVideo]) {
setVideoAvailable(true);
if (!videoTrack)
videoTrack = assetTrack;
}
}
}
// Get the native size of the video, and reset the bounds of the player layer
AVPlayerLayer *playerLayer = [(AVFMediaPlayerSessionObserver*)m_observer playerLayer];
if (videoTrack && playerLayer) {
playerLayer.bounds = CGRectMake(0.0f, 0.0f,
videoTrack.naturalSize.width,
videoTrack.naturalSize.height);
if (m_videoOutput && m_state != QMediaPlayer::StoppedState) {
m_videoOutput->setLayer(playerLayer);
}
}
}
qint64 currentDuration = duration();
if (m_duration != currentDuration)
Q_EMIT durationChanged(m_duration = currentDuration);
if (m_audioAvailable != isAudioAvailable())
Q_EMIT audioAvailableChanged(m_audioAvailable = !m_audioAvailable);
if (m_videoAvailable != isVideoAvailable())
Q_EMIT videoAvailableChanged(m_videoAvailable = !m_videoAvailable);
newStatus = isPlaying ? QMediaPlayer::BufferedMedia : QMediaPlayer::LoadedMedia;
if (m_state == QMediaPlayer::PlayingState && [(AVFMediaPlayerSessionObserver*)m_observer player]) {
@@ -835,17 +837,3 @@ void AVFMediaPlayerSession::processMediaLoadError()
Q_EMIT mediaStatusChanged(m_mediaStatus = QMediaPlayer::InvalidMedia);
Q_EMIT stateChanged(m_state = QMediaPlayer::StoppedState);
}
void AVFMediaPlayerSession::processCurrentItemChanged()
{
#ifdef QT_DEBUG_AVF
qDebug() << Q_FUNC_INFO;
#endif
AVPlayerLayer *playerLayer = [(AVFMediaPlayerSessionObserver*)m_observer playerLayer];
if (m_videoOutput && m_state != QMediaPlayer::StoppedState) {
m_videoOutput->setLayer(playerLayer);
}
}

View File

@@ -64,8 +64,10 @@ AVFVideoWidget::~AVFVideoWidget()
qDebug() << Q_FUNC_INFO;
#endif
if (m_playerLayer)
if (m_playerLayer) {
[m_playerLayer removeFromSuperlayer];
[m_playerLayer release];
}
}
QSize AVFVideoWidget::sizeHint() const

View File

@@ -61,8 +61,10 @@ AVFVideoWindowControl::AVFVideoWindowControl(QObject *parent)
AVFVideoWindowControl::~AVFVideoWindowControl()
{
if (m_playerLayer)
if (m_playerLayer) {
[m_playerLayer removeFromSuperlayer];
[m_playerLayer release];
}
}
WId AVFVideoWindowControl::winId() const

View File

@@ -45,6 +45,7 @@
# include "coreaudiosessionmanager.h"
#endif
#include <QtCore/QDataStream>
#include <QtCore/QDebug>
#include <QtCore/QSet>

View File

@@ -38,6 +38,7 @@
#include <CoreAudio/CoreAudioTypes.h>
#include <AudioToolbox/AudioToolbox.h>
#include <QtCore/QIODevice>
#include <QtCore/QWaitCondition>
#include <QtCore/QMutex>
#include <QtCore/QTimer>

View File

@@ -52,6 +52,7 @@
#endif
#include <QtMultimedia/private/qaudiohelpers_p.h>
#include <QtCore/QDataStream>
#include <QtCore/QDebug>
QT_BEGIN_NAMESPACE

View File

@@ -41,6 +41,7 @@
#include <AudioUnit/AudioUnit.h>
#include <CoreAudio/CoreAudioTypes.h>
#include <QtCore/QIODevice>
#include <QtCore/QWaitCondition>
#include <QtCore/QMutex>

View File

@@ -43,6 +43,7 @@
#include "coreaudiodeviceinfo.h"
#include "coreaudioutils.h"
#include <QtCore/QDataStream>
#include <QtCore/QTimer>
#include <QtCore/QDebug>
@@ -698,14 +699,14 @@ void CoreAudioOutput::audioThreadStop()
{
stopTimers();
if (m_audioThreadState.testAndSetAcquire(Running, Stopped))
m_threadFinished.wait(&m_mutex);
m_threadFinished.wait(&m_mutex, 500);
}
void CoreAudioOutput::audioThreadDrain()
{
stopTimers();
if (m_audioThreadState.testAndSetAcquire(Running, Draining))
m_threadFinished.wait(&m_mutex);
m_threadFinished.wait(&m_mutex, 500);
}
void CoreAudioOutput::audioDeviceStop()

View File

@@ -117,4 +117,28 @@ GstEncodingProfile *CameraBinAudioEncoder::createProfile()
return profile;
}
void CameraBinAudioEncoder::applySettings(GstElement *encoder)
{
GObjectClass * const objectClass = G_OBJECT_GET_CLASS(encoder);
const char * const name = gst_plugin_feature_get_name(
GST_PLUGIN_FEATURE(gst_element_get_factory(encoder)));
const bool isVorbis = qstrcmp(name, "vorbisenc") == 0;
const int bitRate = m_actualAudioSettings.bitRate();
if (!isVorbis && bitRate == -1) {
// Bit rate is invalid, don't evaluate the remaining conditions unless the encoder is
// vorbisenc which is known to accept -1 as an unspecified bitrate.
} else if (g_object_class_find_property(objectClass, "bitrate")) {
g_object_set(G_OBJECT(encoder), "bitrate", bitRate, NULL);
} else if (g_object_class_find_property(objectClass, "target-bitrate")) {
g_object_set(G_OBJECT(encoder), "target-bitrate", bitRate, NULL);
}
if (isVorbis) {
static const double qualities[] = { 0.1, 0.3, 0.5, 0.7, 1.0 };
g_object_set(G_OBJECT(encoder), "quality", qualities[m_actualAudioSettings.quality()], NULL);
}
}
QT_END_NAMESPACE

View File

@@ -78,6 +78,8 @@ public:
GstEncodingProfile *createProfile();
void applySettings(GstElement *element);
Q_SIGNALS:
void settingsChanged();

View File

@@ -38,13 +38,9 @@
#include "camerabinserviceplugin.h"
#include "camerabinservice.h"
#include <private/qgstutils_p.h>
#include <private/qcore_unix_p.h>
#include <linux/videodev2.h>
QT_BEGIN_NAMESPACE
template <typename T, int N> static int lengthOf(const T(&)[N]) { return N; }

View File

@@ -98,6 +98,8 @@
#define CAPTURE_START "start-capture"
#define CAPTURE_STOP "stop-capture"
#define FILESINK_BIN_NAME "videobin-filesink"
#define CAMERABIN_IMAGE_MODE 1
#define CAMERABIN_VIDEO_MODE 2
@@ -133,6 +135,7 @@ CameraBinSession::CameraBinSession(GstElementFactory *sourceFactory, QObject *pa
m_capsFilter(0),
m_fileSink(0),
m_audioEncoder(0),
m_videoEncoder(0),
m_muxer(0)
{
if (m_sourceFactory)
@@ -140,6 +143,8 @@ CameraBinSession::CameraBinSession(GstElementFactory *sourceFactory, QObject *pa
m_camerabin = gst_element_factory_make("camerabin2", "camerabin2");
g_signal_connect(G_OBJECT(m_camerabin), "notify::idle", G_CALLBACK(updateBusyStatus), this);
g_signal_connect(G_OBJECT(m_camerabin), "element-added", G_CALLBACK(elementAdded), this);
g_signal_connect(G_OBJECT(m_camerabin), "element-removed", G_CALLBACK(elementRemoved), this);
qt_gst_object_ref_sink(m_camerabin);
m_bus = gst_element_get_bus(m_camerabin);
@@ -344,6 +349,9 @@ void CameraBinSession::setupCaptureResolution()
} else {
g_object_set(m_camerabin, VIEWFINDER_CAPS_PROPERTY, NULL, NULL);
}
if (m_videoEncoder)
m_videoEncodeControl->applySettings(m_videoEncoder);
}
void CameraBinSession::setAudioCaptureCaps()
@@ -370,6 +378,9 @@ void CameraBinSession::setAudioCaptureCaps()
GstCaps *caps = gst_caps_new_full(structure, NULL);
g_object_set(G_OBJECT(m_camerabin), AUDIO_CAPTURE_CAPS_PROPERTY, caps, NULL);
gst_caps_unref(caps);
if (m_audioEncoder)
m_audioEncodeControl->applySettings(m_audioEncoder);
}
GstElement *CameraBinSession::buildCameraSource()
@@ -712,13 +723,19 @@ void CameraBinSession::updateBusyStatus(GObject *o, GParamSpec *p, gpointer d)
qint64 CameraBinSession::duration() const
{
GstFormat format = GST_FORMAT_TIME;
gint64 duration = 0;
if (m_camerabin) {
GstElement *fileSink = gst_bin_get_by_name(GST_BIN(m_camerabin), FILESINK_BIN_NAME);
if (fileSink) {
GstFormat format = GST_FORMAT_TIME;
gint64 duration = 0;
bool ret = gst_element_query_position(fileSink, &format, &duration);
gst_object_unref(GST_OBJECT(fileSink));
if (ret)
return duration / 1000000;
}
}
if ( m_camerabin && gst_element_query_position(m_camerabin, &format, &duration))
return duration / 1000000;
else
return 0;
return 0;
}
bool CameraBinSession::isMuted() const
@@ -1293,4 +1310,32 @@ QList<QSize> CameraBinSession::supportedResolutions(QPair<int,int> rate,
return res;
}
void CameraBinSession::elementAdded(GstBin *, GstElement *element, CameraBinSession *session)
{
GstElementFactory *factory = gst_element_get_factory(element);
if (GST_IS_BIN(element)) {
g_signal_connect(G_OBJECT(element), "element-added", G_CALLBACK(elementAdded), session);
g_signal_connect(G_OBJECT(element), "element-removed", G_CALLBACK(elementRemoved), session);
} else if (!factory) {
// no-op
} else if (gst_element_factory_list_is_type(factory, GST_ELEMENT_FACTORY_TYPE_AUDIO_ENCODER)) {
session->m_audioEncoder = element;
session->m_audioEncodeControl->applySettings(element);
} else if (gst_element_factory_list_is_type(factory, GST_ELEMENT_FACTORY_TYPE_VIDEO_ENCODER)) {
session->m_videoEncoder = element;
session->m_videoEncodeControl->applySettings(element);
}
}
void CameraBinSession::elementRemoved(GstBin *, GstElement *element, CameraBinSession *session)
{
if (element == session->m_audioEncoder)
session->m_audioEncoder = 0;
else if (element == session->m_videoEncoder)
session->m_videoEncoder = 0;
}
QT_END_NAMESPACE

View File

@@ -190,6 +190,9 @@ private:
void setAudioCaptureCaps();
static void updateBusyStatus(GObject *o, GParamSpec *p, gpointer d);
static void elementAdded(GstBin *bin, GstElement *element, CameraBinSession *session);
static void elementRemoved(GstBin *bin, GstElement *element, CameraBinSession *session);
QUrl m_sink;
QUrl m_actualSink;
bool m_recordingActive;
@@ -241,6 +244,7 @@ private:
GstElement *m_capsFilter;
GstElement *m_fileSink;
GstElement *m_audioEncoder;
GstElement *m_videoEncoder;
GstElement *m_muxer;
public:

View File

@@ -175,4 +175,46 @@ GstEncodingProfile *CameraBinVideoEncoder::createProfile()
return (GstEncodingProfile *)profile;
}
void CameraBinVideoEncoder::applySettings(GstElement *encoder)
{
GObjectClass * const objectClass = G_OBJECT_GET_CLASS(encoder);
const char * const name = gst_plugin_feature_get_name(
GST_PLUGIN_FEATURE(gst_element_get_factory(encoder)));
const int bitRate = m_actualVideoSettings.bitRate();
if (bitRate == -1) {
// Bit rate is invalid, don't evaluate the remaining conditions.
} else if (g_object_class_find_property(objectClass, "bitrate")) {
g_object_set(G_OBJECT(encoder), "bitrate", bitRate, NULL);
} else if (g_object_class_find_property(objectClass, "target-bitrate")) {
g_object_set(G_OBJECT(encoder), "target-bitrate", bitRate, NULL);
}
if (qstrcmp(name, "theoraenc") == 0) {
static const int qualities[] = { 8, 16, 32, 45, 60 };
g_object_set(G_OBJECT(encoder), "quality", qualities[m_actualVideoSettings.quality()], NULL);
} else if (qstrncmp(name, "avenc_", 6) == 0) {
if (g_object_class_find_property(objectClass, "pass")) {
static const int modes[] = { 0, 2, 512, 1024 };
g_object_set(G_OBJECT(encoder), "pass", modes[m_actualVideoSettings.encodingMode()], NULL);
}
if (g_object_class_find_property(objectClass, "quantizer")) {
static const double qualities[] = { 20, 8.0, 3.0, 2.5, 2.0 };
g_object_set(G_OBJECT(encoder), "quantizer", qualities[m_actualVideoSettings.quality()], NULL);
}
} else if (qstrncmp(name, "omx", 3) == 0) {
if (!g_object_class_find_property(objectClass, "control-rate")) {
} else switch (m_actualVideoSettings.encodingMode()) {
case QMultimedia::ConstantBitRateEncoding:
g_object_set(G_OBJECT(encoder), "control-rate", 2, NULL);
break;
case QMultimedia::AverageBitRateEncoding:
g_object_set(G_OBJECT(encoder), "control-rate", 1, NULL);
break;
default:
g_object_set(G_OBJECT(encoder), "control-rate", 0, NULL);
}
}
}
QT_END_NAMESPACE

View File

@@ -76,6 +76,8 @@ public:
GstEncodingProfile *createProfile();
void applySettings(GstElement *encoder);
Q_SIGNALS:
void settingsChanged();

View File

@@ -15,7 +15,6 @@ HEADERS += $$PWD/qgstreamercaptureservice.h \
$$PWD/qgstreamerrecordercontrol.h \
$$PWD/qgstreamermediacontainercontrol.h \
$$PWD/qgstreamercameracontrol.h \
$$PWD/qgstreamerv4l2input.h \
$$PWD/qgstreamercapturemetadatacontrol.h \
$$PWD/qgstreamerimagecapturecontrol.h \
$$PWD/qgstreamerimageencode.h \
@@ -28,7 +27,6 @@ SOURCES += $$PWD/qgstreamercaptureservice.cpp \
$$PWD/qgstreamerrecordercontrol.cpp \
$$PWD/qgstreamermediacontainercontrol.cpp \
$$PWD/qgstreamercameracontrol.cpp \
$$PWD/qgstreamerv4l2input.cpp \
$$PWD/qgstreamercapturemetadatacontrol.cpp \
$$PWD/qgstreamerimagecapturecontrol.cpp \
$$PWD/qgstreamerimageencode.cpp \
@@ -37,13 +35,18 @@ SOURCES += $$PWD/qgstreamercaptureservice.cpp \
# Camera usage with gstreamer needs to have
#CONFIG += use_gstreamer_camera
use_gstreamer_camera {
DEFINES += USE_GSTREAMER_CAMERA
use_gstreamer_camera:config_linux_v4l {
DEFINES += USE_GSTREAMER_CAMERA
OTHER_FILES += \
mediacapturecamera.json
HEADERS += \
$$PWD/qgstreamerv4l2input.h
SOURCES += \
$$PWD/qgstreamerv4l2input.cpp
OTHER_FILES += \
mediacapturecamera.json
} else {
OTHER_FILES += \
mediacapture.json
OTHER_FILES += \
mediacapture.json
}

View File

@@ -40,9 +40,12 @@
#include "qgstreamerimageencode.h"
#include "qgstreamercameracontrol.h"
#include <private/qgstreamerbushelper_p.h>
#include "qgstreamerv4l2input.h"
#include "qgstreamercapturemetadatacontrol.h"
#if defined(USE_GSTREAMER_CAMERA)
#include "qgstreamerv4l2input.h"
#endif
#include "qgstreamerimagecapturecontrol.h"
#include <private/qgstreameraudioinputselector_p.h>
#include <private/qgstreamervideoinputdevicecontrol_p.h>
@@ -66,7 +69,9 @@ QGstreamerCaptureService::QGstreamerCaptureService(const QString &service, QObje
m_cameraControl = 0;
m_metaDataControl = 0;
#if defined(USE_GSTREAMER_CAMERA)
m_videoInput = 0;
#endif
m_audioInputSelector = 0;
m_videoInputDevice = 0;
@@ -82,6 +87,7 @@ QGstreamerCaptureService::QGstreamerCaptureService(const QString &service, QObje
m_captureSession = new QGstreamerCaptureSession(QGstreamerCaptureSession::Audio, this);
}
#if defined(USE_GSTREAMER_CAMERA)
if (service == Q_MEDIASERVICE_CAMERA) {
m_captureSession = new QGstreamerCaptureSession(QGstreamerCaptureSession::AudioAndVideo, this);
m_cameraControl = new QGstreamerCameraControl(m_captureSession);
@@ -103,6 +109,7 @@ QGstreamerCaptureService::QGstreamerCaptureService(const QString &service, QObje
#endif
m_imageCaptureControl = new QGstreamerImageCaptureControl(m_captureSession);
}
#endif
m_audioInputSelector = new QGstreamerAudioInputSelector(this);
connect(m_audioInputSelector, SIGNAL(activeInputChanged(QString)), m_captureSession, SLOT(setCaptureDevice(QString)));

View File

@@ -70,7 +70,9 @@ private:
QGstreamerCaptureSession *m_captureSession;
QGstreamerCameraControl *m_cameraControl;
#if defined(USE_GSTREAMER_CAMERA)
QGstreamerV4L2Input *m_videoInput;
#endif
QGstreamerCaptureMetaDataControl *m_metaDataControl;
QAudioInputSelectorControl *m_audioInputSelector;

View File

@@ -76,8 +76,10 @@ static const QGstreamerMetaDataKeyLookup *qt_gstreamerMetaDataKeys()
// Music
metadataKeys->insert(GST_TAG_ALBUM, QMediaMetaData::AlbumTitle);
metadataKeys->insert(GST_TAG_ARTIST, QMediaMetaData::AlbumArtist);
metadataKeys->insert(GST_TAG_PERFORMER, QMediaMetaData::ContributingArtist);
#if (GST_VERSION_MAJOR >= 0) && (GST_VERSION_MINOR >= 10) && (GST_VERSION_MICRO >= 25)
metadataKeys->insert(GST_TAG_ALBUM_ARTIST, QMediaMetaData::AlbumArtist);
#endif
metadataKeys->insert(GST_TAG_ARTIST, QMediaMetaData::ContributingArtist);
#if (GST_VERSION_MAJOR >= 0) && (GST_VERSION_MINOR >= 10) && (GST_VERSION_MICRO >= 19)
metadataKeys->insert(GST_TAG_COMPOSER, QMediaMetaData::Composer);
#endif
@@ -164,6 +166,11 @@ void QGstreamerMetaDataProvider::updateTags()
}
}
if (oldTags.isEmpty() != m_tags.isEmpty()) {
emit metaDataAvailableChanged(isMetaDataAvailable());
changed = true;
}
if (changed)
emit metaDataChanged();
}

View File

@@ -60,7 +60,6 @@ QGstreamerPlayerControl::QGstreamerPlayerControl(QGstreamerPlayerSession *sessio
, m_currentState(QMediaPlayer::StoppedState)
, m_mediaStatus(QMediaPlayer::NoMedia)
, m_bufferProgress(-1)
, m_seekToStartPending(false)
, m_pendingSeekPosition(-1)
, m_setMediaPending(false)
, m_stream(0)
@@ -69,7 +68,7 @@ QGstreamerPlayerControl::QGstreamerPlayerControl(QGstreamerPlayerSession *sessio
Q_ASSERT(m_resources);
connect(m_session, SIGNAL(positionChanged(qint64)),
this, SLOT(updatePosition(qint64)));
this, SIGNAL(positionChanged(qint64)));
connect(m_session, SIGNAL(durationChanged(qint64)),
this, SIGNAL(durationChanged(qint64)));
connect(m_session, SIGNAL(mutedStateChanged(bool)),
@@ -94,8 +93,6 @@ QGstreamerPlayerControl::QGstreamerPlayerControl(QGstreamerPlayerSession *sessio
this, SLOT(handleInvalidMedia()));
connect(m_session, SIGNAL(playbackRateChanged(qreal)),
this, SIGNAL(playbackRateChanged(qreal)));
connect(m_session, SIGNAL(seekableChanged(bool)),
this, SLOT(applyPendingSeek(bool)));
connect(m_resources, SIGNAL(resourcesGranted()), SLOT(handleResourcesGranted()));
//denied signal should be queued to have correct state update process,
@@ -117,7 +114,7 @@ QMediaPlayerResourceSetInterface* QGstreamerPlayerControl::resources() const
qint64 QGstreamerPlayerControl::position() const
{
return m_seekToStartPending ? 0 : m_session->position();
return m_pendingSeekPosition != -1 ? m_pendingSeekPosition : m_session->position();
}
qint64 QGstreamerPlayerControl::duration() const
@@ -183,15 +180,21 @@ void QGstreamerPlayerControl::setPosition(qint64 pos)
if (m_mediaStatus == QMediaPlayer::EndOfMedia) {
m_mediaStatus = QMediaPlayer::LoadedMedia;
m_seekToStartPending = true;
}
if (m_session->isSeekable() && m_session->seek(pos)) {
m_seekToStartPending = false;
} else {
if (m_currentState == QMediaPlayer::StoppedState) {
m_pendingSeekPosition = pos;
//don't display the first video frame since it's not what user requested.
m_session->showPrerollFrames(false);
emit positionChanged(m_pendingSeekPosition);
} else if (m_session->isSeekable()) {
m_session->showPrerollFrames(true);
m_session->seek(pos);
m_pendingSeekPosition = -1;
} else if (m_session->state() == QMediaPlayer::StoppedState) {
m_pendingSeekPosition = pos;
emit positionChanged(m_pendingSeekPosition);
} else if (m_pendingSeekPosition != -1) {
m_pendingSeekPosition = -1;
emit positionChanged(m_pendingSeekPosition);
}
popAndNotifyState();
@@ -239,26 +242,30 @@ void QGstreamerPlayerControl::playOrPause(QMediaPlayer::State newState)
}
#endif
if (m_mediaStatus == QMediaPlayer::EndOfMedia && m_pendingSeekPosition == -1) {
m_pendingSeekPosition = 0;
}
if (!m_resources->isGranted())
m_resources->acquire();
if (m_resources->isGranted()) {
if (m_seekToStartPending) {
// show prerolled frame if switching from stopped state
if (m_pendingSeekPosition == -1) {
m_session->showPrerollFrames(true);
} else if (m_session->state() == QMediaPlayer::StoppedState) {
// Don't evaluate the next two conditions.
} else if (m_session->isSeekable()) {
m_session->pause();
if (!m_session->seek(0)) {
m_bufferProgress = -1;
m_session->stop();
m_mediaStatus = QMediaPlayer::LoadingMedia;
}
m_seekToStartPending = false;
m_session->showPrerollFrames(true);
m_session->seek(m_pendingSeekPosition);
m_pendingSeekPosition = -1;
} else {
m_pendingSeekPosition = -1;
}
bool ok = false;
// show prerolled frame if switching from stopped state
if (newState != QMediaPlayer::StoppedState && m_currentState == QMediaPlayer::StoppedState && m_pendingSeekPosition == -1)
m_session->showPrerollFrames(true);
//To prevent displaying the first video frame when playback is resumed
//the pipeline is paused instead of playing, seeked to requested position,
//and after seeking is finished (position updated) playback is restarted
@@ -305,7 +312,7 @@ void QGstreamerPlayerControl::stop()
m_session->pause();
if (m_mediaStatus != QMediaPlayer::EndOfMedia) {
m_seekToStartPending = true;
m_pendingSeekPosition = 0;
emit positionChanged(position());
}
}
@@ -343,7 +350,7 @@ void QGstreamerPlayerControl::setMedia(const QMediaContent &content, QIODevice *
m_currentState = QMediaPlayer::StoppedState;
QMediaContent oldMedia = m_currentResource;
m_pendingSeekPosition = -1;
m_pendingSeekPosition = 0;
m_session->showPrerollFrames(false); // do not show prerolled frames until pause() or play() explicitly called
m_setMediaPending = false;
@@ -390,7 +397,6 @@ void QGstreamerPlayerControl::setMedia(const QMediaContent &content, QIODevice *
m_currentResource = content;
m_stream = stream;
m_seekToStartPending = false;
QNetworkRequest request;
@@ -462,8 +468,21 @@ void QGstreamerPlayerControl::updateSessionState(QMediaPlayer::State state)
{
pushState();
if (state == QMediaPlayer::StoppedState)
if (state == QMediaPlayer::StoppedState) {
m_session->showPrerollFrames(false);
m_currentState = QMediaPlayer::StoppedState;
}
if (state == QMediaPlayer::PausedState && m_currentState != QMediaPlayer::StoppedState) {
if (m_pendingSeekPosition != -1 && m_session->isSeekable()) {
m_session->showPrerollFrames(true);
m_session->seek(m_pendingSeekPosition);
}
m_pendingSeekPosition = -1;
if (m_currentState == QMediaPlayer::PlayingState)
m_session->play();
}
updateMediaStatus();
@@ -512,7 +531,6 @@ void QGstreamerPlayerControl::processEOS()
m_mediaStatus = QMediaPlayer::EndOfMedia;
emit positionChanged(position());
m_session->endOfMediaReset();
m_setMediaPending = true;
if (m_currentState != QMediaPlayer::StoppedState) {
m_currentState = QMediaPlayer::StoppedState;
@@ -549,17 +567,12 @@ void QGstreamerPlayerControl::setBufferProgress(int progress)
emit bufferStatusChanged(m_bufferProgress);
}
void QGstreamerPlayerControl::applyPendingSeek(bool isSeekable)
{
if (isSeekable && m_pendingSeekPosition != -1)
setPosition(m_pendingSeekPosition);
}
void QGstreamerPlayerControl::handleInvalidMedia()
{
pushState();
m_mediaStatus = QMediaPlayer::InvalidMedia;
m_currentState = QMediaPlayer::StoppedState;
m_setMediaPending = true;
popAndNotifyState();
}
@@ -636,24 +649,4 @@ void QGstreamerPlayerControl::popAndNotifyState()
}
}
void QGstreamerPlayerControl::updatePosition(qint64 pos)
{
#ifdef DEBUG_PLAYBIN
qDebug() << Q_FUNC_INFO << pos/1000.0 << "pending:" << m_pendingSeekPosition/1000.0;
#endif
if (m_pendingSeekPosition != -1) {
//seek request is complete, it's safe to resume playback
//with prerolled frame displayed
m_pendingSeekPosition = -1;
if (m_currentState != QMediaPlayer::StoppedState)
m_session->showPrerollFrames(true);
if (m_currentState == QMediaPlayer::PlayingState) {
m_session->play();
}
}
emit positionChanged(pos);
}
QT_END_NAMESPACE

Some files were not shown because too many files have changed in this diff Show More