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

Conflicts:
	tests/auto/unit/qaudioformat/tst_qaudioformat.cpp

Change-Id: I8499473569df9eac8c7069160e42ed477dacad4d
This commit is contained in:
Frederik Gladhorn
2014-04-11 14:21:32 +02:00
48 changed files with 690 additions and 405 deletions

View File

@@ -2,9 +2,10 @@ TEMPLATE = lib
TARGET = qgsttools_p TARGET = qgsttools_p
QPRO_PWD = $$PWD QPRO_PWD = $$PWD
QT = core multimedia-private gui-private QT = core-private multimedia-private gui-private
!static:DEFINES += QT_MAKEDLL !static:DEFINES += QT_MAKEDLL
DEFINES += GLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_26
unix:!maemo*:contains(QT_CONFIG, alsa) { unix:!maemo*:contains(QT_CONFIG, alsa) {
DEFINES += HAVE_ALSA DEFINES += HAVE_ALSA

View File

@@ -44,16 +44,7 @@
#include <QtCore/QDir> #include <QtCore/QDir>
#include <QtCore/QDebug> #include <QtCore/QDebug>
#include <linux/types.h> #include <private/qcore_unix_p.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <sys/poll.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <linux/videodev2.h> #include <linux/videodev2.h>
QGstreamerVideoInputDeviceControl::QGstreamerVideoInputDeviceControl(QObject *parent) QGstreamerVideoInputDeviceControl::QGstreamerVideoInputDeviceControl(QObject *parent)
@@ -135,7 +126,7 @@ void QGstreamerVideoInputDeviceControl::update()
foreach( const QFileInfo &entryInfo, entries ) { foreach( const QFileInfo &entryInfo, entries ) {
//qDebug() << "Try" << entryInfo.filePath(); //qDebug() << "Try" << entryInfo.filePath();
int fd = ::open(entryInfo.filePath().toLatin1().constData(), O_RDWR ); int fd = qt_safe_open(entryInfo.filePath().toLatin1().constData(), O_RDWR );
if (fd == -1) if (fd == -1)
continue; continue;
@@ -165,6 +156,6 @@ void QGstreamerVideoInputDeviceControl::update()
m_names.append(entryInfo.filePath()); m_names.append(entryInfo.filePath());
m_descriptions.append(name); m_descriptions.append(name);
} }
::close(fd); qt_safe_close(fd);
} }
} }

View File

@@ -290,15 +290,15 @@ void QDeclarativeAudio::setVolume(qreal volume)
return; return;
} }
if (m_vol == volume) if (this->volume() == volume)
return; return;
m_vol = volume; if (m_complete) {
if (m_complete)
m_player->setVolume(qRound(volume * 100)); m_player->setVolume(qRound(volume * 100));
else } else {
m_vol = volume;
emit volumeChanged(); emit volumeChanged();
}
} }
bool QDeclarativeAudio::isMuted() const bool QDeclarativeAudio::isMuted() const
@@ -308,15 +308,15 @@ bool QDeclarativeAudio::isMuted() const
void QDeclarativeAudio::setMuted(bool muted) void QDeclarativeAudio::setMuted(bool muted)
{ {
if (m_muted == muted) if (isMuted() == muted)
return; return;
m_muted = muted; if (m_complete) {
if (m_complete)
m_player->setMuted(muted); m_player->setMuted(muted);
else } else {
m_muted = muted;
emit mutedChanged(); emit mutedChanged();
}
} }
qreal QDeclarativeAudio::bufferProgress() const qreal QDeclarativeAudio::bufferProgress() const
@@ -331,20 +331,20 @@ bool QDeclarativeAudio::isSeekable() const
qreal QDeclarativeAudio::playbackRate() const qreal QDeclarativeAudio::playbackRate() const
{ {
return m_playbackRate; return m_complete ? m_player->playbackRate() : m_playbackRate;
} }
void QDeclarativeAudio::setPlaybackRate(qreal rate) void QDeclarativeAudio::setPlaybackRate(qreal rate)
{ {
if (m_playbackRate == rate) if (playbackRate() == rate)
return; return;
m_playbackRate = rate; if (m_complete) {
m_player->setPlaybackRate(rate);
if (m_complete) } else {
m_player->setPlaybackRate(m_playbackRate); m_playbackRate = rate;
else
emit playbackRateChanged(); emit playbackRateChanged();
}
} }
QString QDeclarativeAudio::errorString() const QString QDeclarativeAudio::errorString() const
@@ -426,12 +426,12 @@ void QDeclarativeAudio::seek(int position)
if (this->position() == position) if (this->position() == position)
return; return;
m_position = position; if (m_complete) {
m_player->setPosition(position);
if (m_complete) } else {
m_player->setPosition(m_position); m_position = position;
else
emit positionChanged(); emit positionChanged();
}
} }
/*! /*!

View File

@@ -166,6 +166,8 @@ void QWaveDecoder::handleData()
// Swizzle this // Swizzle this
if (bigEndian) { if (bigEndian) {
wave.audioFormat = qFromBigEndian<quint16>(wave.audioFormat); wave.audioFormat = qFromBigEndian<quint16>(wave.audioFormat);
} else {
wave.audioFormat = qFromLittleEndian<quint16>(wave.audioFormat);
} }
if (wave.audioFormat != 0 && wave.audioFormat != 1) { if (wave.audioFormat != 0 && wave.audioFormat != 1) {
@@ -207,6 +209,8 @@ void QWaveDecoder::handleData()
source->read(reinterpret_cast<char *>(&descriptor), sizeof(chunk)); source->read(reinterpret_cast<char *>(&descriptor), sizeof(chunk));
if (bigEndian) if (bigEndian)
descriptor.size = qFromBigEndian<quint32>(descriptor.size); descriptor.size = qFromBigEndian<quint32>(descriptor.size);
else
descriptor.size = qFromLittleEndian<quint32>(descriptor.size);
dataSize = descriptor.size; dataSize = descriptor.size;
@@ -227,13 +231,15 @@ void QWaveDecoder::handleData()
bool QWaveDecoder::enoughDataAvailable() bool QWaveDecoder::enoughDataAvailable()
{ {
chunk descriptor; chunk descriptor;
if (!peekChunk(&descriptor)) if (!peekChunk(&descriptor, false))
return false; return false;
// This is only called for the RIFF/RIFX header, before bigEndian is set, // This is only called for the RIFF/RIFX header, before bigEndian is set,
// so we have to manually swizzle // so we have to manually swizzle
if (qstrncmp(descriptor.id, "RIFX", 4) == 0) if (qstrncmp(descriptor.id, "RIFX", 4) == 0)
descriptor.size = qFromBigEndian<quint32>(descriptor.size); descriptor.size = qFromBigEndian<quint32>(descriptor.size);
if (qstrncmp(descriptor.id, "RIFF", 4) == 0)
descriptor.size = qFromLittleEndian<quint32>(descriptor.size);
if (source->bytesAvailable() < qint64(sizeof(chunk) + descriptor.size)) if (source->bytesAvailable() < qint64(sizeof(chunk) + descriptor.size))
return false; return false;
@@ -270,16 +276,18 @@ bool QWaveDecoder::findChunk(const char *chunkId)
return false; return false;
} }
// Handles endianness bool QWaveDecoder::peekChunk(chunk *pChunk, bool handleEndianness)
bool QWaveDecoder::peekChunk(chunk *pChunk)
{ {
if (source->bytesAvailable() < qint64(sizeof(chunk))) if (source->bytesAvailable() < qint64(sizeof(chunk)))
return false; return false;
source->peek(reinterpret_cast<char *>(pChunk), sizeof(chunk)); source->peek(reinterpret_cast<char *>(pChunk), sizeof(chunk));
if (bigEndian) if (handleEndianness) {
pChunk->size = qFromBigEndian<quint32>(pChunk->size); if (bigEndian)
pChunk->size = qFromBigEndian<quint32>(pChunk->size);
else
pChunk->size = qFromLittleEndian<quint32>(pChunk->size);
}
return true; return true;
} }

View File

@@ -103,7 +103,7 @@ private:
char id[4]; char id[4];
quint32 size; quint32 size;
}; };
bool peekChunk(chunk* pChunk); bool peekChunk(chunk* pChunk, bool handleEndianness = true);
struct RIFFHeader struct RIFFHeader
{ {

View File

@@ -43,7 +43,7 @@ sourcedirs += ../..
excludedirs += ../../multimediawidgets excludedirs += ../../multimediawidgets
depends += qtcore qtdoc qtquick qtqml qtmultimediawidgets depends += qtcore qtdoc qtgui qtquick qtqml qtmultimediawidgets qtwidgets
navigation.landingpage = "Qt Multimedia" navigation.landingpage = "Qt Multimedia"
navigation.cppclassespage = "Qt Multimedia C++ Classes" navigation.cppclassespage = "Qt Multimedia C++ Classes"

View File

@@ -1,6 +1,12 @@
TARGET = QtMultimedia TARGET = QtMultimedia
QT = core-private network gui-private QT = core-private network gui-private
MODULE_PLUGIN_TYPES = \
mediaservice \
audio \
video/videonode \
playlistformats
QMAKE_DOCS = $$PWD/doc/qtmultimedia.qdocconf QMAKE_DOCS = $$PWD/doc/qtmultimedia.qdocconf
load(qt_module) load(qt_module)
@@ -67,11 +73,6 @@ ANDROID_FEATURES += \
android.hardware.camera \ android.hardware.camera \
android.hardware.camera.autofocus \ android.hardware.camera.autofocus \
android.hardware.microphone android.hardware.microphone
MODULE_PLUGIN_TYPES = \
mediaservice \
audio \
video/videonode \
playlistformats
win32: LIBS_PRIVATE += -luuid win32: LIBS_PRIVATE += -luuid

View File

@@ -10,7 +10,7 @@ JAVASOURCES += $$PWD/src/org/qtproject/qt5/android/multimedia/QtAndroidMediaPlay
$$PWD/src/org/qtproject/qt5/android/multimedia/QtSurfaceTextureListener.java \ $$PWD/src/org/qtproject/qt5/android/multimedia/QtSurfaceTextureListener.java \
$$PWD/src/org/qtproject/qt5/android/multimedia/QtSurfaceTextureHolder.java \ $$PWD/src/org/qtproject/qt5/android/multimedia/QtSurfaceTextureHolder.java \
$$PWD/src/org/qtproject/qt5/android/multimedia/QtMultimediaUtils.java \ $$PWD/src/org/qtproject/qt5/android/multimedia/QtMultimediaUtils.java \
$$PWD/src/org/qtproject/qt5/android/multimedia/QtMediaRecorder.java $$PWD/src/org/qtproject/qt5/android/multimedia/QtMediaRecorderListener.java
# install # install
target.path = $$[QT_INSTALL_PREFIX]/jar target.path = $$[QT_INSTALL_PREFIX]/jar

View File

@@ -43,16 +43,13 @@ package org.qtproject.qt5.android.multimedia;
import android.media.MediaRecorder; import android.media.MediaRecorder;
public class QtMediaRecorder extends MediaRecorder implements MediaRecorder.OnErrorListener, MediaRecorder.OnInfoListener public class QtMediaRecorderListener implements MediaRecorder.OnErrorListener, MediaRecorder.OnInfoListener
{ {
private long m_id = -1; private long m_id = -1;
public QtMediaRecorder(long id) public QtMediaRecorderListener(long id)
{ {
super();
m_id = id; m_id = id;
setOnErrorListener(this);
setOnInfoListener(this);
} }
@Override @Override

View File

@@ -47,6 +47,7 @@
#include "qandroidmultimediautils.h" #include "qandroidmultimediautils.h"
#include <qthread.h> #include <qthread.h>
#include <qmutex.h> #include <qmutex.h>
#include <QtCore/private/qjnihelpers_p.h>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
@@ -145,7 +146,7 @@ private:
QJNIObjectPrivate lastCamera; QJNIObjectPrivate lastCamera;
}; };
class JCameraWorker : public QObject, public QJNIObjectPrivate class JCameraWorker : public QObject
{ {
Q_OBJECT Q_OBJECT
friend class JCamera; friend class JCamera;
@@ -230,12 +231,11 @@ class JCameraWorker : public QObject, public QJNIObjectPrivate
QSize m_previewSize; QSize m_previewSize;
int m_rotation; int m_rotation;
bool m_hasAPI14;
JCamera *q; JCamera *q;
QThread *m_workerThread; QThread *m_workerThread;
QMutex m_parametersMutex; QMutex m_parametersMutex;
QJNIObjectPrivate m_camera;
Q_SIGNALS: Q_SIGNALS:
void previewSizeChanged(); void previewSizeChanged();
@@ -269,7 +269,7 @@ JCamera::JCamera(int cameraId, jobject cam, QThread *workerThread)
JCamera::~JCamera() JCamera::~JCamera()
{ {
if (d->isValid()) { if (d->m_camera.isValid()) {
g_objectMapMutex.lock(); g_objectMapMutex.lock();
g_objectMap.remove(d->m_cameraId); g_objectMap.remove(d->m_cameraId);
g_objectMapMutex.unlock(); g_objectMapMutex.unlock();
@@ -595,7 +595,7 @@ void JCamera::fetchLastPreviewFrame()
QJNIObjectPrivate JCamera::getCameraObject() QJNIObjectPrivate JCamera::getCameraObject()
{ {
return d->getObjectField("m_camera", "Landroid/hardware/Camera;"); return d->m_camera.getObjectField("m_camera", "Landroid/hardware/Camera;");
} }
void JCamera::startPreview() void JCamera::startPreview()
@@ -613,47 +613,29 @@ void JCamera::stopPreview()
JCameraWorker::JCameraWorker(JCamera *camera, int cameraId, jobject cam, QThread *workerThread) JCameraWorker::JCameraWorker(JCamera *camera, int cameraId, jobject cam, QThread *workerThread)
: QObject(0) : QObject(0)
, QJNIObjectPrivate(cam)
, m_cameraId(cameraId) , m_cameraId(cameraId)
, m_rotation(0) , m_rotation(0)
, m_hasAPI14(false)
, m_parametersMutex(QMutex::Recursive) , m_parametersMutex(QMutex::Recursive)
, m_camera(cam)
{ {
q = camera; q = camera;
m_workerThread = workerThread; m_workerThread = workerThread;
moveToThread(m_workerThread); moveToThread(m_workerThread);
if (isValid()) { if (m_camera.isValid()) {
g_objectMapMutex.lock(); g_objectMapMutex.lock();
g_objectMap.insert(cameraId, q); g_objectMap.insert(cameraId, q);
g_objectMapMutex.unlock(); g_objectMapMutex.unlock();
m_info = QJNIObjectPrivate("android/hardware/Camera$CameraInfo"); m_info = QJNIObjectPrivate("android/hardware/Camera$CameraInfo");
callStaticMethod<void>("android/hardware/Camera", m_camera.callStaticMethod<void>("android/hardware/Camera",
"getCameraInfo", "getCameraInfo",
"(ILandroid/hardware/Camera$CameraInfo;)V", "(ILandroid/hardware/Camera$CameraInfo;)V",
cameraId, m_info.object()); cameraId, m_info.object());
QJNIObjectPrivate params = callObjectMethod("getParameters", QJNIObjectPrivate params = m_camera.callObjectMethod("getParameters",
"()Landroid/hardware/Camera$Parameters;"); "()Landroid/hardware/Camera$Parameters;");
m_parameters = QJNIObjectPrivate(params); m_parameters = QJNIObjectPrivate(params);
// Check if API 14 is available
QJNIEnvironmentPrivate env;
jclass clazz = env->FindClass("android/hardware/Camera");
if (env->ExceptionCheck()) {
clazz = 0;
env->ExceptionClear();
}
if (clazz) {
// startFaceDetection() was added in API 14
jmethodID id = env->GetMethodID(clazz, "startFaceDetection", "()V");
if (env->ExceptionCheck()) {
id = 0;
env->ExceptionClear();
}
m_hasAPI14 = bool(id);
}
} }
} }
@@ -668,7 +650,7 @@ void JCameraWorker::release()
m_parametersMutex.lock(); m_parametersMutex.lock();
m_parameters = QJNIObjectPrivate(); m_parameters = QJNIObjectPrivate();
m_parametersMutex.unlock(); m_parametersMutex.unlock();
callMethod<void>("release"); m_camera.callMethod<void>("release");
} }
JCamera::CameraFacing JCameraWorker::getFacing() JCamera::CameraFacing JCameraWorker::getFacing()
@@ -752,7 +734,9 @@ void JCameraWorker::updatePreviewSize()
void JCameraWorker::setPreviewTexture(void *surfaceTexture) void JCameraWorker::setPreviewTexture(void *surfaceTexture)
{ {
callMethod<void>("setPreviewTexture", "(Landroid/graphics/SurfaceTexture;)V", static_cast<jobject>(surfaceTexture)); m_camera.callMethod<void>("setPreviewTexture",
"(Landroid/graphics/SurfaceTexture;)V",
static_cast<jobject>(surfaceTexture));
} }
bool JCameraWorker::isZoomSupported() bool JCameraWorker::isZoomSupported()
@@ -878,9 +862,12 @@ void JCameraWorker::setFocusMode(const QString &value)
int JCameraWorker::getMaxNumFocusAreas() int JCameraWorker::getMaxNumFocusAreas()
{ {
if (QtAndroidPrivate::androidSdkVersion() < 14)
return 0;
QMutexLocker parametersLocker(&m_parametersMutex); QMutexLocker parametersLocker(&m_parametersMutex);
if (!m_hasAPI14 || !m_parameters.isValid()) if (!m_parameters.isValid())
return 0; return 0;
return m_parameters.callMethod<jint>("getMaxNumFocusAreas"); return m_parameters.callMethod<jint>("getMaxNumFocusAreas");
@@ -888,11 +875,14 @@ int JCameraWorker::getMaxNumFocusAreas()
QList<QRect> JCameraWorker::getFocusAreas() QList<QRect> JCameraWorker::getFocusAreas()
{ {
QMutexLocker parametersLocker(&m_parametersMutex);
QList<QRect> areas; QList<QRect> areas;
if (m_hasAPI14 && m_parameters.isValid()) { if (QtAndroidPrivate::androidSdkVersion() < 14)
return areas;
QMutexLocker parametersLocker(&m_parametersMutex);
if (m_parameters.isValid()) {
QJNIObjectPrivate list = m_parameters.callObjectMethod("getFocusAreas", QJNIObjectPrivate list = m_parameters.callObjectMethod("getFocusAreas",
"()Ljava/util/List;"); "()Ljava/util/List;");
@@ -913,9 +903,12 @@ QList<QRect> JCameraWorker::getFocusAreas()
void JCameraWorker::setFocusAreas(const QList<QRect> &areas) void JCameraWorker::setFocusAreas(const QList<QRect> &areas)
{ {
if (QtAndroidPrivate::androidSdkVersion() < 14)
return;
QMutexLocker parametersLocker(&m_parametersMutex); QMutexLocker parametersLocker(&m_parametersMutex);
if (!m_hasAPI14 || !m_parameters.isValid()) if (!m_parameters.isValid())
return; return;
QJNIObjectPrivate list; QJNIObjectPrivate list;
@@ -940,15 +933,18 @@ void JCameraWorker::setFocusAreas(const QList<QRect> &areas)
void JCameraWorker::autoFocus() void JCameraWorker::autoFocus()
{ {
callMethod<void>("autoFocus"); m_camera.callMethod<void>("autoFocus");
emit autoFocusStarted(); emit autoFocusStarted();
} }
bool JCameraWorker::isAutoExposureLockSupported() bool JCameraWorker::isAutoExposureLockSupported()
{ {
if (QtAndroidPrivate::androidSdkVersion() < 14)
return false;
QMutexLocker parametersLocker(&m_parametersMutex); QMutexLocker parametersLocker(&m_parametersMutex);
if (!m_hasAPI14 || !m_parameters.isValid()) if (!m_parameters.isValid())
return false; return false;
return m_parameters.callMethod<jboolean>("isAutoExposureLockSupported"); return m_parameters.callMethod<jboolean>("isAutoExposureLockSupported");
@@ -956,9 +952,12 @@ bool JCameraWorker::isAutoExposureLockSupported()
bool JCameraWorker::getAutoExposureLock() bool JCameraWorker::getAutoExposureLock()
{ {
if (QtAndroidPrivate::androidSdkVersion() < 14)
return false;
QMutexLocker parametersLocker(&m_parametersMutex); QMutexLocker parametersLocker(&m_parametersMutex);
if (!m_hasAPI14 || !m_parameters.isValid()) if (!m_parameters.isValid())
return false; return false;
return m_parameters.callMethod<jboolean>("getAutoExposureLock"); return m_parameters.callMethod<jboolean>("getAutoExposureLock");
@@ -966,9 +965,12 @@ bool JCameraWorker::getAutoExposureLock()
void JCameraWorker::setAutoExposureLock(bool toggle) void JCameraWorker::setAutoExposureLock(bool toggle)
{ {
if (QtAndroidPrivate::androidSdkVersion() < 14)
return;
QMutexLocker parametersLocker(&m_parametersMutex); QMutexLocker parametersLocker(&m_parametersMutex);
if (!m_hasAPI14 || !m_parameters.isValid()) if (!m_parameters.isValid())
return; return;
m_parameters.callMethod<void>("setAutoExposureLock", "(Z)V", toggle); m_parameters.callMethod<void>("setAutoExposureLock", "(Z)V", toggle);
@@ -977,9 +979,12 @@ void JCameraWorker::setAutoExposureLock(bool toggle)
bool JCameraWorker::isAutoWhiteBalanceLockSupported() bool JCameraWorker::isAutoWhiteBalanceLockSupported()
{ {
if (QtAndroidPrivate::androidSdkVersion() < 14)
return false;
QMutexLocker parametersLocker(&m_parametersMutex); QMutexLocker parametersLocker(&m_parametersMutex);
if (!m_hasAPI14 || !m_parameters.isValid()) if (!m_parameters.isValid())
return false; return false;
return m_parameters.callMethod<jboolean>("isAutoWhiteBalanceLockSupported"); return m_parameters.callMethod<jboolean>("isAutoWhiteBalanceLockSupported");
@@ -987,9 +992,12 @@ bool JCameraWorker::isAutoWhiteBalanceLockSupported()
bool JCameraWorker::getAutoWhiteBalanceLock() bool JCameraWorker::getAutoWhiteBalanceLock()
{ {
if (QtAndroidPrivate::androidSdkVersion() < 14)
return false;
QMutexLocker parametersLocker(&m_parametersMutex); QMutexLocker parametersLocker(&m_parametersMutex);
if (!m_hasAPI14 || !m_parameters.isValid()) if (!m_parameters.isValid())
return false; return false;
return m_parameters.callMethod<jboolean>("getAutoWhiteBalanceLock"); return m_parameters.callMethod<jboolean>("getAutoWhiteBalanceLock");
@@ -997,9 +1005,12 @@ bool JCameraWorker::getAutoWhiteBalanceLock()
void JCameraWorker::setAutoWhiteBalanceLock(bool toggle) void JCameraWorker::setAutoWhiteBalanceLock(bool toggle)
{ {
if (QtAndroidPrivate::androidSdkVersion() < 14)
return;
QMutexLocker parametersLocker(&m_parametersMutex); QMutexLocker parametersLocker(&m_parametersMutex);
if (!m_hasAPI14 || !m_parameters.isValid()) if (!m_parameters.isValid())
return; return;
m_parameters.callMethod<void>("setAutoWhiteBalanceLock", "(Z)V", toggle); m_parameters.callMethod<void>("setAutoWhiteBalanceLock", "(Z)V", toggle);
@@ -1184,15 +1195,15 @@ void JCameraWorker::stopPreview()
void JCameraWorker::fetchEachFrame(bool fetch) void JCameraWorker::fetchEachFrame(bool fetch)
{ {
callMethod<void>("fetchEachFrame", "(Z)V", fetch); m_camera.callMethod<void>("fetchEachFrame", "(Z)V", fetch);
} }
void JCameraWorker::fetchLastPreviewFrame() void JCameraWorker::fetchLastPreviewFrame()
{ {
QJNIEnvironmentPrivate env; QJNIEnvironmentPrivate env;
QJNIObjectPrivate dataObj = callObjectMethod("lockAndFetchPreviewBuffer", "()[B"); QJNIObjectPrivate dataObj = m_camera.callObjectMethod("lockAndFetchPreviewBuffer", "()[B");
if (!dataObj.object()) { if (!dataObj.object()) {
callMethod<void>("unlockPreviewBuffer"); m_camera.callMethod<void>("unlockPreviewBuffer");
return; return;
} }
jbyteArray data = static_cast<jbyteArray>(dataObj.object()); jbyteArray data = static_cast<jbyteArray>(dataObj.object());
@@ -1200,16 +1211,16 @@ void JCameraWorker::fetchLastPreviewFrame()
int arrayLength = env->GetArrayLength(data); int arrayLength = env->GetArrayLength(data);
bytes.resize(arrayLength); bytes.resize(arrayLength);
env->GetByteArrayRegion(data, 0, arrayLength, (jbyte*)bytes.data()); env->GetByteArrayRegion(data, 0, arrayLength, (jbyte*)bytes.data());
callMethod<void>("unlockPreviewBuffer"); m_camera.callMethod<void>("unlockPreviewBuffer");
emit previewFetched(bytes); emit previewFetched(bytes);
} }
void JCameraWorker::applyParameters() void JCameraWorker::applyParameters()
{ {
callMethod<void>("setParameters", m_camera.callMethod<void>("setParameters",
"(Landroid/hardware/Camera$Parameters;)V", "(Landroid/hardware/Camera$Parameters;)V",
m_parameters.object()); m_parameters.object());
} }
QStringList JCameraWorker::callParametersStringListMethod(const QByteArray &methodName) QStringList JCameraWorker::callParametersStringListMethod(const QByteArray &methodName)
@@ -1239,7 +1250,7 @@ QStringList JCameraWorker::callParametersStringListMethod(const QByteArray &meth
void JCameraWorker::callVoidMethod(const QByteArray &methodName) void JCameraWorker::callVoidMethod(const QByteArray &methodName)
{ {
callMethod<void>(methodName.constData()); m_camera.callMethod<void>(methodName.constData());
} }

View File

@@ -47,46 +47,55 @@
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
static jclass g_qtMediaRecorderClass = 0; static jclass g_qtMediaRecorderListenerClass = 0;
static QMap<jlong, JMediaRecorder*> g_objectMap; typedef QMap<jlong, JMediaRecorder*> MediaRecorderMap;
Q_GLOBAL_STATIC(MediaRecorderMap, mediaRecorders)
static void notifyError(JNIEnv* , jobject, jlong id, jint what, jint extra) static void notifyError(JNIEnv* , jobject, jlong id, jint what, jint extra)
{ {
JMediaRecorder *obj = g_objectMap.value(id, 0); JMediaRecorder *obj = mediaRecorders->value(id, 0);
if (obj) if (obj)
emit obj->error(what, extra); emit obj->error(what, extra);
} }
static void notifyInfo(JNIEnv* , jobject, jlong id, jint what, jint extra) static void notifyInfo(JNIEnv* , jobject, jlong id, jint what, jint extra)
{ {
JMediaRecorder *obj = g_objectMap.value(id, 0); JMediaRecorder *obj = mediaRecorders->value(id, 0);
if (obj) if (obj)
emit obj->info(what, extra); emit obj->info(what, extra);
} }
JMediaRecorder::JMediaRecorder() JMediaRecorder::JMediaRecorder()
: QObject() : QObject()
, QJNIObjectPrivate(g_qtMediaRecorderClass, "(J)V", reinterpret_cast<jlong>(this))
, m_id(reinterpret_cast<jlong>(this)) , m_id(reinterpret_cast<jlong>(this))
{ {
if (isValid()) m_mediaRecorder = QJNIObjectPrivate("android/media/MediaRecorder");
g_objectMap.insert(m_id, this); if (m_mediaRecorder.isValid()) {
QJNIObjectPrivate listener(g_qtMediaRecorderListenerClass, "(J)V", m_id);
m_mediaRecorder.callMethod<void>("setOnErrorListener",
"(Landroid/media/MediaRecorder$OnErrorListener;)V",
listener.object());
m_mediaRecorder.callMethod<void>("setOnInfoListener",
"(Landroid/media/MediaRecorder$OnInfoListener;)V",
listener.object());
mediaRecorders->insert(m_id, this);
}
} }
JMediaRecorder::~JMediaRecorder() JMediaRecorder::~JMediaRecorder()
{ {
g_objectMap.remove(m_id); mediaRecorders->remove(m_id);
} }
void JMediaRecorder::release() void JMediaRecorder::release()
{ {
callMethod<void>("release"); m_mediaRecorder.callMethod<void>("release");
} }
bool JMediaRecorder::prepare() bool JMediaRecorder::prepare()
{ {
QJNIEnvironmentPrivate env; QJNIEnvironmentPrivate env;
callMethod<void>("prepare"); m_mediaRecorder.callMethod<void>("prepare");
if (env->ExceptionCheck()) { if (env->ExceptionCheck()) {
#ifdef QT_DEBUG #ifdef QT_DEBUG
env->ExceptionDescribe(); env->ExceptionDescribe();
@@ -99,13 +108,13 @@ bool JMediaRecorder::prepare()
void JMediaRecorder::reset() void JMediaRecorder::reset()
{ {
callMethod<void>("reset"); m_mediaRecorder.callMethod<void>("reset");
} }
bool JMediaRecorder::start() bool JMediaRecorder::start()
{ {
QJNIEnvironmentPrivate env; QJNIEnvironmentPrivate env;
callMethod<void>("start"); m_mediaRecorder.callMethod<void>("start");
if (env->ExceptionCheck()) { if (env->ExceptionCheck()) {
#ifdef QT_DEBUG #ifdef QT_DEBUG
env->ExceptionDescribe(); env->ExceptionDescribe();
@@ -119,7 +128,7 @@ bool JMediaRecorder::start()
void JMediaRecorder::stop() void JMediaRecorder::stop()
{ {
QJNIEnvironmentPrivate env; QJNIEnvironmentPrivate env;
callMethod<void>("stop"); m_mediaRecorder.callMethod<void>("stop");
if (env->ExceptionCheck()) { if (env->ExceptionCheck()) {
#ifdef QT_DEBUG #ifdef QT_DEBUG
env->ExceptionDescribe(); env->ExceptionDescribe();
@@ -130,13 +139,13 @@ void JMediaRecorder::stop()
void JMediaRecorder::setAudioChannels(int numChannels) void JMediaRecorder::setAudioChannels(int numChannels)
{ {
callMethod<void>("setAudioChannels", "(I)V", numChannels); m_mediaRecorder.callMethod<void>("setAudioChannels", "(I)V", numChannels);
} }
void JMediaRecorder::setAudioEncoder(AudioEncoder encoder) void JMediaRecorder::setAudioEncoder(AudioEncoder encoder)
{ {
QJNIEnvironmentPrivate env; QJNIEnvironmentPrivate env;
callMethod<void>("setAudioEncoder", "(I)V", int(encoder)); m_mediaRecorder.callMethod<void>("setAudioEncoder", "(I)V", int(encoder));
if (env->ExceptionCheck()) { if (env->ExceptionCheck()) {
#ifdef QT_DEBUG #ifdef QT_DEBUG
env->ExceptionDescribe(); env->ExceptionDescribe();
@@ -147,18 +156,18 @@ void JMediaRecorder::setAudioEncoder(AudioEncoder encoder)
void JMediaRecorder::setAudioEncodingBitRate(int bitRate) void JMediaRecorder::setAudioEncodingBitRate(int bitRate)
{ {
callMethod<void>("setAudioEncodingBitRate", "(I)V", bitRate); m_mediaRecorder.callMethod<void>("setAudioEncodingBitRate", "(I)V", bitRate);
} }
void JMediaRecorder::setAudioSamplingRate(int samplingRate) void JMediaRecorder::setAudioSamplingRate(int samplingRate)
{ {
callMethod<void>("setAudioSamplingRate", "(I)V", samplingRate); m_mediaRecorder.callMethod<void>("setAudioSamplingRate", "(I)V", samplingRate);
} }
void JMediaRecorder::setAudioSource(AudioSource source) void JMediaRecorder::setAudioSource(AudioSource source)
{ {
QJNIEnvironmentPrivate env; QJNIEnvironmentPrivate env;
callMethod<void>("setAudioSource", "(I)V", int(source)); m_mediaRecorder.callMethod<void>("setAudioSource", "(I)V", int(source));
if (env->ExceptionCheck()) { if (env->ExceptionCheck()) {
#ifdef QT_DEBUG #ifdef QT_DEBUG
env->ExceptionDescribe(); env->ExceptionDescribe();
@@ -170,13 +179,13 @@ void JMediaRecorder::setAudioSource(AudioSource source)
void JMediaRecorder::setCamera(JCamera *camera) void JMediaRecorder::setCamera(JCamera *camera)
{ {
QJNIObjectPrivate cam = camera->getCameraObject(); QJNIObjectPrivate cam = camera->getCameraObject();
callMethod<void>("setCamera", "(Landroid/hardware/Camera;)V", cam.object()); m_mediaRecorder.callMethod<void>("setCamera", "(Landroid/hardware/Camera;)V", cam.object());
} }
void JMediaRecorder::setVideoEncoder(VideoEncoder encoder) void JMediaRecorder::setVideoEncoder(VideoEncoder encoder)
{ {
QJNIEnvironmentPrivate env; QJNIEnvironmentPrivate env;
callMethod<void>("setVideoEncoder", "(I)V", int(encoder)); m_mediaRecorder.callMethod<void>("setVideoEncoder", "(I)V", int(encoder));
if (env->ExceptionCheck()) { if (env->ExceptionCheck()) {
#ifdef QT_DEBUG #ifdef QT_DEBUG
env->ExceptionDescribe(); env->ExceptionDescribe();
@@ -187,13 +196,13 @@ void JMediaRecorder::setVideoEncoder(VideoEncoder encoder)
void JMediaRecorder::setVideoEncodingBitRate(int bitRate) void JMediaRecorder::setVideoEncodingBitRate(int bitRate)
{ {
callMethod<void>("setVideoEncodingBitRate", "(I)V", bitRate); m_mediaRecorder.callMethod<void>("setVideoEncodingBitRate", "(I)V", bitRate);
} }
void JMediaRecorder::setVideoFrameRate(int rate) void JMediaRecorder::setVideoFrameRate(int rate)
{ {
QJNIEnvironmentPrivate env; QJNIEnvironmentPrivate env;
callMethod<void>("setVideoFrameRate", "(I)V", rate); m_mediaRecorder.callMethod<void>("setVideoFrameRate", "(I)V", rate);
if (env->ExceptionCheck()) { if (env->ExceptionCheck()) {
#ifdef QT_DEBUG #ifdef QT_DEBUG
env->ExceptionDescribe(); env->ExceptionDescribe();
@@ -205,7 +214,7 @@ void JMediaRecorder::setVideoFrameRate(int rate)
void JMediaRecorder::setVideoSize(const QSize &size) void JMediaRecorder::setVideoSize(const QSize &size)
{ {
QJNIEnvironmentPrivate env; QJNIEnvironmentPrivate env;
callMethod<void>("setVideoSize", "(II)V", size.width(), size.height()); m_mediaRecorder.callMethod<void>("setVideoSize", "(II)V", size.width(), size.height());
if (env->ExceptionCheck()) { if (env->ExceptionCheck()) {
#ifdef QT_DEBUG #ifdef QT_DEBUG
env->ExceptionDescribe(); env->ExceptionDescribe();
@@ -217,7 +226,7 @@ void JMediaRecorder::setVideoSize(const QSize &size)
void JMediaRecorder::setVideoSource(VideoSource source) void JMediaRecorder::setVideoSource(VideoSource source)
{ {
QJNIEnvironmentPrivate env; QJNIEnvironmentPrivate env;
callMethod<void>("setVideoSource", "(I)V", int(source)); m_mediaRecorder.callMethod<void>("setVideoSource", "(I)V", int(source));
if (env->ExceptionCheck()) { if (env->ExceptionCheck()) {
#ifdef QT_DEBUG #ifdef QT_DEBUG
env->ExceptionDescribe(); env->ExceptionDescribe();
@@ -229,7 +238,7 @@ void JMediaRecorder::setVideoSource(VideoSource source)
void JMediaRecorder::setOrientationHint(int degrees) void JMediaRecorder::setOrientationHint(int degrees)
{ {
QJNIEnvironmentPrivate env; QJNIEnvironmentPrivate env;
callMethod<void>("setOrientationHint", "(I)V", degrees); m_mediaRecorder.callMethod<void>("setOrientationHint", "(I)V", degrees);
if (env->ExceptionCheck()) { if (env->ExceptionCheck()) {
#ifdef QT_DEBUG #ifdef QT_DEBUG
env->ExceptionDescribe(); env->ExceptionDescribe();
@@ -241,7 +250,7 @@ void JMediaRecorder::setOrientationHint(int degrees)
void JMediaRecorder::setOutputFormat(OutputFormat format) void JMediaRecorder::setOutputFormat(OutputFormat format)
{ {
QJNIEnvironmentPrivate env; QJNIEnvironmentPrivate env;
callMethod<void>("setOutputFormat", "(I)V", int(format)); m_mediaRecorder.callMethod<void>("setOutputFormat", "(I)V", int(format));
if (env->ExceptionCheck()) { if (env->ExceptionCheck()) {
#ifdef QT_DEBUG #ifdef QT_DEBUG
env->ExceptionDescribe(); env->ExceptionDescribe();
@@ -253,9 +262,9 @@ void JMediaRecorder::setOutputFormat(OutputFormat format)
void JMediaRecorder::setOutputFile(const QString &path) void JMediaRecorder::setOutputFile(const QString &path)
{ {
QJNIEnvironmentPrivate env; QJNIEnvironmentPrivate env;
callMethod<void>("setOutputFile", m_mediaRecorder.callMethod<void>("setOutputFile",
"(Ljava/lang/String;)V", "(Ljava/lang/String;)V",
QJNIObjectPrivate::fromString(path).object()); QJNIObjectPrivate::fromString(path).object());
if (env->ExceptionCheck()) { if (env->ExceptionCheck()) {
#ifdef QT_DEBUG #ifdef QT_DEBUG
env->ExceptionDescribe(); env->ExceptionDescribe();
@@ -271,13 +280,13 @@ static JNINativeMethod methods[] = {
bool JMediaRecorder::initJNI(JNIEnv *env) bool JMediaRecorder::initJNI(JNIEnv *env)
{ {
jclass clazz = env->FindClass("org/qtproject/qt5/android/multimedia/QtMediaRecorder"); jclass clazz = env->FindClass("org/qtproject/qt5/android/multimedia/QtMediaRecorderListener");
if (env->ExceptionCheck()) if (env->ExceptionCheck())
env->ExceptionClear(); env->ExceptionClear();
if (clazz) { if (clazz) {
g_qtMediaRecorderClass = static_cast<jclass>(env->NewGlobalRef(clazz)); g_qtMediaRecorderListenerClass = static_cast<jclass>(env->NewGlobalRef(clazz));
if (env->RegisterNatives(g_qtMediaRecorderClass, if (env->RegisterNatives(g_qtMediaRecorderListenerClass,
methods, methods,
sizeof(methods) / sizeof(methods[0])) < 0) { sizeof(methods) / sizeof(methods[0])) < 0) {
return false; return false;

View File

@@ -50,7 +50,7 @@ QT_BEGIN_NAMESPACE
class JCamera; class JCamera;
class JMediaRecorder : public QObject, public QJNIObjectPrivate class JMediaRecorder : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
@@ -127,6 +127,7 @@ Q_SIGNALS:
private: private:
jlong m_id; jlong m_id;
QJNIObjectPrivate m_mediaRecorder;
}; };
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@@ -2,6 +2,7 @@ TARGET = qtsgvideonode_android
QT += quick multimedia-private qtmultimediaquicktools-private QT += quick multimedia-private qtmultimediaquicktools-private
PLUGIN_TYPE = video/videonode PLUGIN_TYPE = video/videonode
PLUGIN_EXTENDS = quick
PLUGIN_CLASS_NAME = QAndroidSGVideoNodeFactoryPlugin PLUGIN_CLASS_NAME = QAndroidSGVideoNodeFactoryPlugin
load(qt_plugin) load(qt_plugin)

View File

@@ -196,38 +196,14 @@ QList<int> CoreAudioDeviceInfo::supportedSampleRates()
QList<int> CoreAudioDeviceInfo::supportedChannelCounts() QList<int> CoreAudioDeviceInfo::supportedChannelCounts()
{ {
QList<int> supportedChannels; static QList<int> supportedChannels;
int maxChannels = 0;
#if defined(Q_OS_OSX) if (supportedChannels.isEmpty()) {
UInt32 propSize = 0; // If the number of channels is not supported by an audio device, Core Audio will
AudioObjectPropertyScope scope = m_mode == QAudio::AudioInput ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput; // automatically convert the audio data.
AudioObjectPropertyAddress streamConfigurationPropertyAddress = { kAudioDevicePropertyStreamConfiguration, for (int i = 1; i <= 16; ++i)
scope, supportedChannels.append(i);
kAudioObjectPropertyElementMaster };
if (AudioObjectGetPropertyDataSize(m_deviceId, &streamConfigurationPropertyAddress, 0, NULL, &propSize) == noErr) {
AudioBufferList* audioBufferList = static_cast<AudioBufferList*>(malloc(propSize));
if (audioBufferList != 0) {
if (AudioObjectGetPropertyData(m_deviceId, &streamConfigurationPropertyAddress, 0, NULL, &propSize, audioBufferList) == noErr) {
for (int i = 0; i < int(audioBufferList->mNumberBuffers); ++i)
maxChannels += audioBufferList->mBuffers[i].mNumberChannels;
}
free(audioBufferList);
}
} }
#else //iOS
if (m_mode == QAudio::AudioInput)
maxChannels = CoreAudioSessionManager::instance().inputChannelCount();
else if (m_mode == QAudio::AudioOutput)
maxChannels = CoreAudioSessionManager::instance().outputChannelCount();
#endif
// Assume all channel configurations are supported up to the maximum number of channels
for (int i = 1; i <= maxChannels; ++i)
supportedChannels.append(i);
return supportedChannels; return supportedChannels;
} }

View File

@@ -92,9 +92,6 @@ public:
QList<QByteArray> inputDevices(); QList<QByteArray> inputDevices();
QList<QByteArray> outputDevices(); QList<QByteArray> outputDevices();
int inputChannelCount();
int outputChannelCount();
float currentIOBufferDuration(); float currentIOBufferDuration();
float preferredSampleRate(); float preferredSampleRate();

View File

@@ -377,16 +377,6 @@ QList<QByteArray> CoreAudioSessionManager::outputDevices()
return outputDevices; return outputDevices;
} }
int CoreAudioSessionManager::inputChannelCount()
{
return [[m_sessionObserver audioSession] inputNumberOfChannels];
}
int CoreAudioSessionManager::outputChannelCount()
{
return [[m_sessionObserver audioSession] outputNumberOfChannels];
}
float CoreAudioSessionManager::currentIOBufferDuration() float CoreAudioSessionManager::currentIOBufferDuration()
{ {
return [[m_sessionObserver audioSession] IOBufferDuration]; return [[m_sessionObserver audioSession] IOBufferDuration];

View File

@@ -83,6 +83,7 @@ DirectShowPlayerControl::DirectShowPlayerControl(DirectShowPlayerService *servic
, m_streamTypes(0) , m_streamTypes(0)
, m_muteVolume(-1) , m_muteVolume(-1)
, m_position(0) , m_position(0)
, m_pendingPosition(-1)
, m_duration(0) , m_duration(0)
, m_playbackRate(0) , m_playbackRate(0)
, m_seekable(false) , m_seekable(false)
@@ -112,12 +113,22 @@ qint64 DirectShowPlayerControl::duration() const
qint64 DirectShowPlayerControl::position() const qint64 DirectShowPlayerControl::position() const
{ {
if (m_pendingPosition != -1)
return m_pendingPosition;
return const_cast<qint64 &>(m_position) = m_service->position(); return const_cast<qint64 &>(m_position) = m_service->position();
} }
void DirectShowPlayerControl::setPosition(qint64 position) void DirectShowPlayerControl::setPosition(qint64 position)
{ {
if (m_state == QMediaPlayer::StoppedState && m_pendingPosition != position) {
m_pendingPosition = position;
emit positionChanged(m_pendingPosition);
return;
}
m_service->seek(position); m_service->seek(position);
m_pendingPosition = -1;
} }
int DirectShowPlayerControl::volume() const int DirectShowPlayerControl::volume() const
@@ -253,6 +264,8 @@ void DirectShowPlayerControl::play()
return; return;
} }
m_service->play(); m_service->play();
if (m_pendingPosition != -1)
setPosition(m_pendingPosition);
emit stateChanged(m_state = QMediaPlayer::PlayingState); emit stateChanged(m_state = QMediaPlayer::PlayingState);
} }

View File

@@ -135,6 +135,7 @@ private:
int m_streamTypes; int m_streamTypes;
int m_muteVolume; int m_muteVolume;
qint64 m_position; qint64 m_position;
qint64 m_pendingPosition;
qint64 m_duration; qint64 m_duration;
qreal m_playbackRate; qreal m_playbackRate;
bool m_seekable; bool m_seekable;

View File

@@ -49,19 +49,6 @@
#include <QtCore/QDir> #include <QtCore/QDir>
#include <QtCore/QDebug> #include <QtCore/QDebug>
#include <linux/types.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <sys/poll.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <linux/videodev2.h>
#include <gst/gst.h>
// #define QT_SUPPORTEDMIMETYPES_DEBUG // #define QT_SUPPORTEDMIMETYPES_DEBUG
QMediaService* QGstreamerAudioDecoderServicePlugin::create(const QString &key) QMediaService* QGstreamerAudioDecoderServicePlugin::create(const QString &key)

View File

@@ -50,18 +50,6 @@
#include <QtCore/qfile.h> #include <QtCore/qfile.h>
#include <QtCore/qmetaobject.h> #include <QtCore/qmetaobject.h>
#include <linux/types.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <sys/poll.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <linux/videodev2.h>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
//#define CAMEABIN_DEBUG 1 //#define CAMEABIN_DEBUG 1

View File

@@ -50,18 +50,8 @@
#include "camerabinservice.h" #include "camerabinservice.h"
#include <private/qgstutils_p.h> #include <private/qgstutils_p.h>
#include <linux/types.h> #include <private/qcore_unix_p.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <sys/poll.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <linux/videodev2.h> #include <linux/videodev2.h>
#include <gst/gst.h>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
@@ -148,7 +138,7 @@ void CameraBinServicePlugin::updateDevices() const
QFileInfoList entries = devDir.entryInfoList(QStringList() << "video*"); QFileInfoList entries = devDir.entryInfoList(QStringList() << "video*");
foreach (const QFileInfo &entryInfo, entries) { foreach (const QFileInfo &entryInfo, entries) {
int fd = ::open(entryInfo.filePath().toLatin1().constData(), O_RDWR ); int fd = qt_safe_open(entryInfo.filePath().toLatin1().constData(), O_RDWR );
if (fd == -1) if (fd == -1)
continue; continue;
@@ -178,7 +168,7 @@ void CameraBinServicePlugin::updateDevices() const
m_cameraDevices.append(entryInfo.filePath().toLocal8Bit()); m_cameraDevices.append(entryInfo.filePath().toLocal8Bit());
m_cameraDescriptions.append(name); m_cameraDescriptions.append(name);
} }
::close(fd); qt_safe_close(fd);
} }
if (!m_cameraDevices.isEmpty()) if (!m_cameraDevices.isEmpty())

View File

@@ -1,5 +1,5 @@
QT += multimedia-private network QT += core-private multimedia-private network
CONFIG += no_private_qt_headers_warning CONFIG += no_private_qt_headers_warning
qtHaveModule(widgets) { qtHaveModule(widgets) {

View File

@@ -51,18 +51,8 @@
#include "qgstreamercaptureservice.h" #include "qgstreamercaptureservice.h"
#include <private/qgstutils_p.h> #include <private/qgstutils_p.h>
#include <linux/types.h> #include <private/qcore_unix_p.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <sys/poll.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <linux/videodev2.h> #include <linux/videodev2.h>
#include <gst/gst.h>
QMediaService* QGstreamerCaptureServicePlugin::create(const QString &key) QMediaService* QGstreamerCaptureServicePlugin::create(const QString &key)
{ {
@@ -155,7 +145,7 @@ void QGstreamerCaptureServicePlugin::updateDevices() const
foreach( const QFileInfo &entryInfo, entries ) { foreach( const QFileInfo &entryInfo, entries ) {
//qDebug() << "Try" << entryInfo.filePath(); //qDebug() << "Try" << entryInfo.filePath();
int fd = ::open(entryInfo.filePath().toLatin1().constData(), O_RDWR ); int fd = qt_safe_open(entryInfo.filePath().toLatin1().constData(), O_RDWR );
if (fd == -1) if (fd == -1)
continue; continue;
@@ -185,7 +175,7 @@ void QGstreamerCaptureServicePlugin::updateDevices() const
m_cameraDevices.append(entryInfo.filePath().toLocal8Bit()); m_cameraDevices.append(entryInfo.filePath().toLocal8Bit());
m_cameraDescriptions.append(name); m_cameraDescriptions.append(name);
} }
::close(fd); qt_safe_close(fd);
} }
if (!m_cameraDevices.isEmpty()) if (!m_cameraDevices.isEmpty())

View File

@@ -44,16 +44,7 @@
#include <QtCore/qdebug.h> #include <QtCore/qdebug.h>
#include <QtCore/qfile.h> #include <QtCore/qfile.h>
#include <linux/types.h> #include <private/qcore_unix_p.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <sys/poll.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <linux/videodev2.h> #include <linux/videodev2.h>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE

View File

@@ -162,25 +162,18 @@ void QGstreamerMetaDataProvider::updateTags()
{ {
QVariantMap oldTags = m_tags; QVariantMap oldTags = m_tags;
m_tags.clear(); m_tags.clear();
bool changed = false;
QSet<QString> allTags = QSet<QString>::fromList(m_tags.keys());
QMapIterator<QByteArray ,QVariant> i(m_session->tags()); QMapIterator<QByteArray ,QVariant> i(m_session->tags());
while (i.hasNext()) { while (i.hasNext()) {
i.next(); i.next();
//use gstreamer native keys for elements not in m_keysMap //use gstreamer native keys for elements not in m_keysMap
QString key = m_keysMap.value(i.key(), i.key()); QString key = m_keysMap.value(i.key(), i.key());
m_tags[key] = i.value(); m_tags.insert(key, i.value());
allTags.insert(key); if (i.value() != oldTags.value(key)) {
} changed = true;
emit metaDataChanged(key, i.value());
bool changed = false; }
foreach (const QString &key, allTags) {
const QVariant value = m_tags.value(key);
if (value != oldTags.value(key)) {
changed = true;
emit metaDataChanged(key, value);
}
} }
if (changed) if (changed)

View File

@@ -51,20 +51,6 @@
#include "qgstreamerplayerservice.h" #include "qgstreamerplayerservice.h"
#include <private/qgstutils_p.h> #include <private/qgstutils_p.h>
#include <linux/types.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <sys/poll.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <linux/videodev2.h>
#include <gst/gst.h>
QMediaService* QGstreamerPlayerServicePlugin::create(const QString &key) QMediaService* QGstreamerPlayerServicePlugin::create(const QString &key)
{ {
QGstUtils::initializeGst(); QGstUtils::initializeGst();

View File

@@ -67,6 +67,20 @@
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
static bool usePlaybinVolume()
{
static enum { Yes, No, Unknown } status = Unknown;
if (status == Unknown) {
QByteArray v = qgetenv("QT_GSTREAMER_USE_PLAYBIN_VOLUME");
bool value = !v.isEmpty() && v != "0" && v != "false";
if (value)
status = Yes;
else
status = No;
}
return status == Yes;
}
typedef enum { typedef enum {
GST_PLAY_FLAG_VIDEO = 0x00000001, GST_PLAY_FLAG_VIDEO = 0x00000001,
GST_PLAY_FLAG_AUDIO = 0x00000002, GST_PLAY_FLAG_AUDIO = 0x00000002,
@@ -104,6 +118,7 @@ QGstreamerPlayerSession::QGstreamerPlayerSession(QObject *parent)
m_pendingVideoSink(0), m_pendingVideoSink(0),
m_nullVideoSink(0), m_nullVideoSink(0),
m_audioSink(0), m_audioSink(0),
m_volumeElement(0),
m_bus(0), m_bus(0),
m_videoOutput(0), m_videoOutput(0),
m_renderer(0), m_renderer(0),
@@ -151,8 +166,28 @@ QGstreamerPlayerSession::QGstreamerPlayerSession(QObject *parent)
#endif #endif
g_object_set(G_OBJECT(m_playbin), "flags", flags, NULL); g_object_set(G_OBJECT(m_playbin), "flags", flags, NULL);
m_audioSink = gst_element_factory_make("autoaudiosink", "audiosink"); GstElement *audioSink = gst_element_factory_make("autoaudiosink", "audiosink");
if (m_audioSink) { if (audioSink) {
if (usePlaybinVolume()) {
m_audioSink = audioSink;
m_volumeElement = m_playbin;
} else {
m_volumeElement = gst_element_factory_make("volume", "volumeelement");
if (m_volumeElement) {
m_audioSink = gst_bin_new("audio-output-bin");
gst_bin_add_many(GST_BIN(m_audioSink), m_volumeElement, audioSink, NULL);
gst_element_link(m_volumeElement, audioSink);
GstPad *pad = gst_element_get_static_pad(m_volumeElement, "sink");
gst_element_add_pad(GST_ELEMENT(m_audioSink), gst_ghost_pad_new("sink", pad));
gst_object_unref(GST_OBJECT(pad));
} else {
m_audioSink = audioSink;
m_volumeElement = m_playbin;
}
}
g_object_set(G_OBJECT(m_playbin), "audio-sink", m_audioSink, NULL); g_object_set(G_OBJECT(m_playbin), "audio-sink", m_audioSink, NULL);
addAudioBufferProbe(); addAudioBufferProbe();
} }
@@ -193,12 +228,12 @@ QGstreamerPlayerSession::QGstreamerPlayerSession(QObject *parent)
g_signal_connect(G_OBJECT(m_playbin), "notify::source", G_CALLBACK(playbinNotifySource), this); g_signal_connect(G_OBJECT(m_playbin), "notify::source", G_CALLBACK(playbinNotifySource), this);
g_signal_connect(G_OBJECT(m_playbin), "element-added", G_CALLBACK(handleElementAdded), this); g_signal_connect(G_OBJECT(m_playbin), "element-added", G_CALLBACK(handleElementAdded), this);
// Init volume and mute state if (usePlaybinVolume()) {
g_object_set(G_OBJECT(m_playbin), "volume", 1.0, NULL); updateVolume();
g_object_set(G_OBJECT(m_playbin), "mute", FALSE, NULL); updateMuted();
g_signal_connect(G_OBJECT(m_playbin), "notify::volume", G_CALLBACK(handleVolumeChange), this);
g_signal_connect(G_OBJECT(m_playbin), "notify::volume", G_CALLBACK(handleVolumeChange), this); g_signal_connect(G_OBJECT(m_playbin), "notify::mute", G_CALLBACK(handleMutedChange), this);
g_signal_connect(G_OBJECT(m_playbin), "notify::mute", G_CALLBACK(handleMutedChange), this); }
g_signal_connect(G_OBJECT(m_playbin), "video-changed", G_CALLBACK(handleStreamsChange), this); g_signal_connect(G_OBJECT(m_playbin), "video-changed", G_CALLBACK(handleStreamsChange), this);
g_signal_connect(G_OBJECT(m_playbin), "audio-changed", G_CALLBACK(handleStreamsChange), this); g_signal_connect(G_OBJECT(m_playbin), "audio-changed", G_CALLBACK(handleStreamsChange), this);
@@ -912,10 +947,8 @@ void QGstreamerPlayerSession::setVolume(int volume)
if (m_volume != volume) { if (m_volume != volume) {
m_volume = volume; m_volume = volume;
if (m_playbin) { if (m_volumeElement)
//playbin2 allows to set volume and muted independently, g_object_set(G_OBJECT(m_volumeElement), "volume", m_volume / 100.0, NULL);
g_object_set(G_OBJECT(m_playbin), "volume", m_volume/100.0, NULL);
}
emit volumeChanged(m_volume); emit volumeChanged(m_volume);
} }
@@ -929,7 +962,9 @@ void QGstreamerPlayerSession::setMuted(bool muted)
if (m_muted != muted) { if (m_muted != muted) {
m_muted = muted; m_muted = muted;
g_object_set(G_OBJECT(m_playbin), "mute", m_muted ? TRUE : FALSE, NULL); if (m_volumeElement)
g_object_set(G_OBJECT(m_volumeElement), "mute", m_muted ? TRUE : FALSE, NULL);
emit mutedStateChanged(m_muted); emit mutedStateChanged(m_muted);
} }
} }
@@ -952,13 +987,15 @@ bool QGstreamerPlayerSession::processBusMessage(const QGstreamerMessage &message
if (gm) { if (gm) {
//tag message comes from elements inside playbin, not from playbin itself //tag message comes from elements inside playbin, not from playbin itself
if (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_TAG) { if (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_TAG) {
//qDebug() << "tag message";
GstTagList *tag_list; GstTagList *tag_list;
gst_message_parse_tag(gm, &tag_list); gst_message_parse_tag(gm, &tag_list);
m_tags.unite(QGstUtils::gstTagListToMap(tag_list));
QMap<QByteArray, QVariant> newTags = QGstUtils::gstTagListToMap(tag_list);
QMap<QByteArray, QVariant>::const_iterator it = newTags.constBegin();
for ( ; it != newTags.constEnd(); ++it)
m_tags.insert(it.key(), it.value()); // overwrite existing tags
gst_tag_list_free(tag_list); gst_tag_list_free(tag_list);
//qDebug() << m_tags;
emit tagsChanged(); emit tagsChanged();
} else if (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_DURATION) { } else if (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_DURATION) {
@@ -1424,13 +1461,6 @@ void QGstreamerPlayerSession::playbinNotifySource(GObject *o, GParamSpec *p, gpo
qDebug() << "Playbin source added:" << G_OBJECT_CLASS_NAME(G_OBJECT_GET_CLASS(source)); qDebug() << "Playbin source added:" << G_OBJECT_CLASS_NAME(G_OBJECT_GET_CLASS(source));
#endif #endif
// Turn off icecast metadata request, will be re-set if in QNetworkRequest
// (souphttpsrc docs say is false by default, but header appears in request
// @version 0.10.21)
if (g_object_class_find_property(G_OBJECT_GET_CLASS(source), "iradio-mode") != 0)
g_object_set(G_OBJECT(source), "iradio-mode", FALSE, NULL);
// Set Headers // Set Headers
const QByteArray userAgentString("User-Agent"); const QByteArray userAgentString("User-Agent");

View File

@@ -211,6 +211,7 @@ private:
GstElement* m_nullVideoSink; GstElement* m_nullVideoSink;
GstElement* m_audioSink; GstElement* m_audioSink;
GstElement* m_volumeElement;
GstBus* m_bus; GstBus* m_bus;
QObject *m_videoOutput; QObject *m_videoOutput;

View File

@@ -41,7 +41,8 @@
#include "bbcameraorientationhandler.h" #include "bbcameraorientationhandler.h"
#include <QAbstractEventDispatcher> #include <QAbstractEventDispatcher>
#include <QCoreApplication> #include <QGuiApplication>
#include <QScreen>
#include <QDebug> #include <QDebug>
#include <bps/orientation.h> #include <bps/orientation.h>
@@ -87,6 +88,10 @@ bool BbCameraOrientationHandler::nativeEventFilter(const QByteArray&, void *mess
const int angle = orientation_event_get_angle(event); const int angle = orientation_event_get_angle(event);
if (angle != m_orientation) { if (angle != m_orientation) {
#ifndef Q_OS_BLACKBERRY_TABLET
if (angle == 180) // The screen does not rotate at 180 degrees
return false;
#endif
m_orientation = angle; m_orientation = angle;
emit orientationChanged(m_orientation); emit orientationChanged(m_orientation);
} }
@@ -94,6 +99,17 @@ bool BbCameraOrientationHandler::nativeEventFilter(const QByteArray&, void *mess
return false; // do not drop the event return false; // do not drop the event
} }
int BbCameraOrientationHandler::viewfinderOrientation() const
{
// On a keyboard device we do not rotate the screen at all
if (qGuiApp->primaryScreen()->nativeOrientation()
!= qGuiApp->primaryScreen()->primaryOrientation()) {
return m_orientation;
}
return 0;
}
int BbCameraOrientationHandler::orientation() const int BbCameraOrientationHandler::orientation() const
{ {
return m_orientation; return m_orientation;

View File

@@ -57,6 +57,8 @@ public:
int orientation() const; int orientation() const;
int viewfinderOrientation() const;
Q_SIGNALS: Q_SIGNALS:
void orientationChanged(int degree); void orientationChanged(int degree);

View File

@@ -140,7 +140,7 @@ BbCameraSession::BbCameraSession(QObject *parent)
connect(this, SIGNAL(captureModeChanged(QCamera::CaptureModes)), SLOT(updateReadyForCapture())); connect(this, SIGNAL(captureModeChanged(QCamera::CaptureModes)), SLOT(updateReadyForCapture()));
connect(m_orientationHandler, SIGNAL(orientationChanged(int)), SLOT(deviceOrientationChanged(int))); connect(m_orientationHandler, SIGNAL(orientationChanged(int)), SLOT(deviceOrientationChanged(int)));
connect(m_windowGrabber, SIGNAL(frameGrabbed(QImage)), SLOT(viewfinderFrameGrabbed(QImage))); connect(m_windowGrabber, SIGNAL(frameGrabbed(QImage, int)), SLOT(viewfinderFrameGrabbed(QImage)));
} }
BbCameraSession::~BbCameraSession() BbCameraSession::~BbCameraSession()
@@ -772,11 +772,16 @@ void BbCameraSession::viewfinderFrameGrabbed(const QImage &image)
{ {
QTransform transform; QTransform transform;
// subtract out the native rotation
transform.rotate(m_nativeCameraOrientation); transform.rotate(m_nativeCameraOrientation);
// subtract out the current device orientation
if (m_device == cameraIdentifierRear())
transform.rotate(360 - m_orientationHandler->viewfinderOrientation());
else
transform.rotate(m_orientationHandler->viewfinderOrientation());
QImage frame = image.copy().transformed(transform); QImage frame = image.copy().transformed(transform);
if (m_device == cameraIdentifierFront())
frame = frame.mirrored(true, false);
QMutexLocker locker(&m_surfaceMutex); QMutexLocker locker(&m_surfaceMutex);
if (m_surface) { if (m_surface) {
@@ -896,7 +901,7 @@ bool BbCameraSession::startViewFinder()
return false; return false;
} }
const int angle = m_orientationHandler->orientation(); const int angle = m_orientationHandler->viewfinderOrientation();
const QSize rotatedSize = ((angle == 0 || angle == 180) ? viewfinderResolution const QSize rotatedSize = ((angle == 0 || angle == 180) ? viewfinderResolution
: viewfinderResolution.transposed()); : viewfinderResolution.transposed());

View File

@@ -47,6 +47,8 @@
#include <QImage> #include <QImage>
#include <qpa/qplatformnativeinterface.h> #include <qpa/qplatformnativeinterface.h>
#include <QOpenGLContext>
#ifdef Q_OS_BLACKBERRY #ifdef Q_OS_BLACKBERRY
#include <bps/event.h> #include <bps/event.h>
#include <bps/screen.h> #include <bps/screen.h>
@@ -57,13 +59,15 @@ QT_BEGIN_NAMESPACE
WindowGrabber::WindowGrabber(QObject *parent) WindowGrabber::WindowGrabber(QObject *parent)
: QObject(parent), : QObject(parent),
m_screenBuffer(0),
m_screenBufferWidth(-1), m_screenBufferWidth(-1),
m_screenBufferHeight(-1), m_screenBufferHeight(-1),
m_active(false), m_active(false),
m_screenContextInitialized(false), m_screenContextInitialized(false),
m_screenPixmapInitialized(false), m_screenPixmapBuffersInitialized(false),
m_screenPixmapBufferInitialized(false) m_currentFrame(0),
m_eglImageSupported(false),
m_eglImagesInitialized(false),
m_eglImageCheck(false)
{ {
// grab the window frame with 60 frames per second // grab the window frame with 60 frames per second
m_timer.setInterval(1000/60); m_timer.setInterval(1000/60);
@@ -76,6 +80,11 @@ WindowGrabber::WindowGrabber(QObject *parent)
WindowGrabber::~WindowGrabber() WindowGrabber::~WindowGrabber()
{ {
QCoreApplication::eventDispatcher()->removeNativeEventFilter(this); QCoreApplication::eventDispatcher()->removeNativeEventFilter(this);
if (eglImagesInitialized()) {
glDeleteTextures(2, imgTextures);
eglDestroyImageKHR(eglGetDisplay(EGL_DEFAULT_DISPLAY), img[0]);
eglDestroyImageKHR(eglGetDisplay(EGL_DEFAULT_DISPLAY), img[1]);
}
} }
void WindowGrabber::setFrameRate(int frameRate) void WindowGrabber::setFrameRate(int frameRate)
@@ -83,6 +92,37 @@ void WindowGrabber::setFrameRate(int frameRate)
m_timer.setInterval(1000/frameRate); m_timer.setInterval(1000/frameRate);
} }
void WindowGrabber::createEglImages()
{
// Do nothing if either egl images are not supported, the screen context is not valid
// or the images are already created
if (!eglImageSupported() || !m_screenContextInitialized || eglImagesInitialized())
return;
glGenTextures(2, imgTextures);
glBindTexture(GL_TEXTURE_2D, imgTextures[0]);
img[0] = eglCreateImageKHR(eglGetDisplay(EGL_DEFAULT_DISPLAY), EGL_NO_CONTEXT,
EGL_NATIVE_PIXMAP_KHR,
m_screenPixmaps[0],
0);
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, img[0]);
glBindTexture(GL_TEXTURE_2D, imgTextures[1]);
img[1] = eglCreateImageKHR(eglGetDisplay(EGL_DEFAULT_DISPLAY), EGL_NO_CONTEXT,
EGL_NATIVE_PIXMAP_KHR,
m_screenPixmaps[1],
0);
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, img[1]);
if (img[0] == 0 || img[1] == 0) {
qWarning() << "Failed to create KHR images" << img[0] << img[1] << strerror(errno) << errno;
m_eglImageSupported = false;
} else {
m_eglImagesInitialized = true;
}
}
void WindowGrabber::setWindowId(const QByteArray &windowId) void WindowGrabber::setWindowId(const QByteArray &windowId)
{ {
m_windowId = windowId; m_windowId = windowId;
@@ -90,6 +130,9 @@ void WindowGrabber::setWindowId(const QByteArray &windowId)
void WindowGrabber::start() void WindowGrabber::start()
{ {
if (m_active)
return;
int result = 0; int result = 0;
#ifdef Q_OS_BLACKBERRY_TABLET #ifdef Q_OS_BLACKBERRY_TABLET
@@ -124,30 +167,21 @@ void WindowGrabber::start()
m_screenContextInitialized = true; m_screenContextInitialized = true;
} }
result = screen_create_pixmap(&m_screenPixmap, m_screenContext); result = screen_create_pixmap(&m_screenPixmaps[0], m_screenContext);
result = screen_create_pixmap(&m_screenPixmaps[1], m_screenContext);
if (result != 0) { if (result != 0) {
cleanup(); cleanup();
qWarning() << "WindowGrabber: cannot create pixmap:" << strerror(errno); qWarning() << "WindowGrabber: cannot create pixmaps:" << strerror(errno);
return;
} else {
m_screenPixmapInitialized = true;
}
const int usage = SCREEN_USAGE_READ | SCREEN_USAGE_NATIVE;
result = screen_set_pixmap_property_iv(m_screenPixmap, SCREEN_PROPERTY_USAGE, &usage);
if (result != 0) {
cleanup();
qWarning() << "WindowGrabber: cannot set pixmap usage:" << strerror(errno);
return; return;
} }
const int format = SCREEN_FORMAT_RGBA8888; const int usage = SCREEN_USAGE_NATIVE;
result = screen_set_pixmap_property_iv(m_screenPixmap, SCREEN_PROPERTY_FORMAT, &format); result = screen_set_pixmap_property_iv(m_screenPixmaps[0], SCREEN_PROPERTY_USAGE, &usage);
if (result != 0) { result |= screen_set_pixmap_property_iv(m_screenPixmaps[1], SCREEN_PROPERTY_USAGE, &usage);
cleanup();
qWarning() << "WindowGrabber: cannot set pixmap format:" << strerror(errno); const int format = SCREEN_FORMAT_RGBX8888;
return; screen_set_pixmap_property_iv(m_screenPixmaps[0], SCREEN_PROPERTY_FORMAT, &format);
} screen_set_pixmap_property_iv(m_screenPixmaps[1], SCREEN_PROPERTY_FORMAT, &format);
int size[2] = { 0, 0 }; int size[2] = { 0, 0 };
@@ -172,37 +206,51 @@ void WindowGrabber::updateFrameSize()
{ {
int size[2] = { m_screenBufferWidth, m_screenBufferHeight }; int size[2] = { m_screenBufferWidth, m_screenBufferHeight };
int result = screen_set_pixmap_property_iv(m_screenPixmap, SCREEN_PROPERTY_BUFFER_SIZE, size); screen_set_pixmap_property_iv(m_screenPixmaps[0], SCREEN_PROPERTY_BUFFER_SIZE, size);
if (result != 0) { if (eglImageSupported())
cleanup(); screen_set_pixmap_property_iv(m_screenPixmaps[1], SCREEN_PROPERTY_BUFFER_SIZE, size);
qWarning() << "WindowGrabber: cannot set pixmap size:" << strerror(errno);
return; int result = screen_create_pixmap_buffer(m_screenPixmaps[0]);
} if (eglImageSupported())
result |= screen_create_pixmap_buffer(m_screenPixmaps[1]);
result = screen_create_pixmap_buffer(m_screenPixmap);
if (result != 0) { if (result != 0) {
cleanup(); cleanup();
qWarning() << "WindowGrabber: cannot create pixmap buffer:" << strerror(errno); qWarning() << "WindowGrabber: cannot create pixmap buffer:" << strerror(errno);
return; return;
} else {
m_screenPixmapBuffersInitialized = true;
}
result = screen_get_pixmap_property_pv(m_screenPixmaps[0], SCREEN_PROPERTY_RENDER_BUFFERS,
(void**)&m_screenPixmapBuffers[0]);
if (eglImageSupported()) {
result |= screen_get_pixmap_property_pv(m_screenPixmaps[1], SCREEN_PROPERTY_RENDER_BUFFERS,
(void**)&m_screenPixmapBuffers[1]);
} }
result = screen_get_pixmap_property_pv(m_screenPixmap, SCREEN_PROPERTY_RENDER_BUFFERS, (void**)&m_screenPixmapBuffer);
if (result != 0) { if (result != 0) {
cleanup(); cleanup();
qWarning() << "WindowGrabber: cannot get pixmap buffer:" << strerror(errno); qWarning() << "WindowGrabber: cannot get pixmap buffer:" << strerror(errno);
return; return;
} else {
m_screenPixmapBufferInitialized = true;
} }
result = screen_get_buffer_property_pv(m_screenPixmapBuffer, SCREEN_PROPERTY_POINTER, (void**)&m_screenBuffer); result = screen_get_buffer_property_pv(m_screenPixmapBuffers[0], SCREEN_PROPERTY_POINTER,
(void**)&m_screenBuffers[0]);
if (eglImageSupported()) {
result |= screen_get_buffer_property_pv(m_screenPixmapBuffers[1], SCREEN_PROPERTY_POINTER,
(void**)&m_screenBuffers[1]);
}
if (result != 0) { if (result != 0) {
cleanup(); cleanup();
qWarning() << "WindowGrabber: cannot get pixmap buffer pointer:" << strerror(errno); qWarning() << "WindowGrabber: cannot get pixmap buffer pointer:" << strerror(errno);
return; return;
} }
result = screen_get_buffer_property_iv(m_screenPixmapBuffer, SCREEN_PROPERTY_STRIDE, &m_screenBufferStride); result = screen_get_buffer_property_iv(m_screenPixmapBuffers[0], SCREEN_PROPERTY_STRIDE,
&m_screenBufferStride);
if (result != 0) { if (result != 0) {
cleanup(); cleanup();
qWarning() << "WindowGrabber: cannot get pixmap buffer stride:" << strerror(errno); qWarning() << "WindowGrabber: cannot get pixmap buffer stride:" << strerror(errno);
@@ -310,8 +358,40 @@ QByteArray WindowGrabber::windowGroupId() const
return QByteArray(groupIdData); return QByteArray(groupIdData);
} }
bool WindowGrabber::eglImageSupported()
{
return m_eglImageSupported;
}
void WindowGrabber::checkForEglImageExtension()
{
QOpenGLContext *m_context = QOpenGLContext::currentContext();
if (!m_context) //Should not happen, because we are called from the render thread
return;
QByteArray eglExtensions = QByteArray(eglQueryString(eglGetDisplay(EGL_DEFAULT_DISPLAY),
EGL_EXTENSIONS));
m_eglImageSupported = m_context->hasExtension(QByteArrayLiteral("GL_OES_EGL_image"))
&& eglExtensions.contains(QByteArrayLiteral("EGL_KHR_image"));
m_eglImageCheck = true;
}
bool WindowGrabber::eglImagesInitialized()
{
return m_eglImagesInitialized;
}
void WindowGrabber::grab() void WindowGrabber::grab()
{ {
if (!m_eglImageCheck) // We did not check for egl images yet
return;
if (eglImageSupported())
m_currentFrame = (m_currentFrame + 1) % 2;
else
m_currentFrame = 0;
int size[2] = { 0, 0 }; int size[2] = { 0, 0 };
int result = screen_get_window_property_iv(m_window, SCREEN_PROPERTY_SOURCE_SIZE, size); int result = screen_get_window_property_iv(m_window, SCREEN_PROPERTY_SOURCE_SIZE, size);
@@ -324,40 +404,33 @@ void WindowGrabber::grab()
if (m_screenBufferWidth != size[0] || m_screenBufferHeight != size[1]) { if (m_screenBufferWidth != size[0] || m_screenBufferHeight != size[1]) {
// The source viewport size changed, so we have to adapt our buffers // The source viewport size changed, so we have to adapt our buffers
if (m_screenPixmapBufferInitialized) { if (m_screenPixmapBuffersInitialized) {
screen_destroy_pixmap_buffer(m_screenPixmap); screen_destroy_pixmap_buffer(m_screenPixmaps[0]);
m_screenPixmapBufferInitialized = false; if (eglImageSupported())
screen_destroy_pixmap_buffer(m_screenPixmaps[1]);
} }
m_screenBufferWidth = size[0]; m_screenBufferWidth = size[0];
m_screenBufferHeight = size[1]; m_screenBufferHeight = size[1];
updateFrameSize(); updateFrameSize();
m_eglImagesInitialized = false;
} }
const int rect[] = { 0, 0, m_screenBufferWidth, m_screenBufferHeight }; const int rect[] = { 0, 0, m_screenBufferWidth, m_screenBufferHeight };
result = screen_read_window(m_window, m_screenPixmapBuffer, 1, rect, 0); result = screen_read_window(m_window, m_screenPixmapBuffers[m_currentFrame], 1, rect, 0);
if (result != 0) if (result != 0)
return; return;
const QImage frame((unsigned char*)m_screenBuffer, m_screenBufferWidth, m_screenBufferHeight, const QImage frame((unsigned char*)m_screenBuffers[m_currentFrame], m_screenBufferWidth,
m_screenBufferStride, QImage::Format_ARGB32); m_screenBufferHeight, m_screenBufferStride, QImage::Format_ARGB32);
emit frameGrabbed(frame); emit frameGrabbed(frame, imgTextures[m_currentFrame]);
} }
void WindowGrabber::cleanup() void WindowGrabber::cleanup()
{ {
if (m_screenPixmapBufferInitialized) { //We only need to destroy the context as it frees all resources associated with it
screen_destroy_buffer(m_screenPixmapBuffer);
m_screenPixmapBufferInitialized = false;
}
if (m_screenPixmapInitialized) {
screen_destroy_pixmap(m_screenPixmap);
m_screenPixmapInitialized = false;
}
if (m_screenContextInitialized) { if (m_screenContextInitialized) {
screen_destroy_context(m_screenContext); screen_destroy_context(m_screenContext);
m_screenContextInitialized = false; m_screenContextInitialized = false;

View File

@@ -41,6 +41,12 @@
#ifndef WINDOWGRABBER_H #ifndef WINDOWGRABBER_H
#define WINDOWGRABBER_H #define WINDOWGRABBER_H
#define EGL_EGLEXT_PROTOTYPES = 1
#define GL_GLEXT_PROTOTYPES = 1
#include <EGL/egl.h>
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include <EGL/eglext.h>
#include <QAbstractNativeEventFilter> #include <QAbstractNativeEventFilter>
#include <QObject> #include <QObject>
#include <QTimer> #include <QTimer>
@@ -59,6 +65,8 @@ public:
void setFrameRate(int frameRate); void setFrameRate(int frameRate);
void createEglImages();
void setWindowId(const QByteArray &windowId); void setWindowId(const QByteArray &windowId);
void start(); void start();
@@ -73,8 +81,12 @@ public:
QByteArray windowGroupId() const; QByteArray windowGroupId() const;
bool eglImageSupported();
void checkForEglImageExtension();
bool eglImagesInitialized();
signals: signals:
void frameGrabbed(const QImage &frame); void frameGrabbed(const QImage &frame, int);
private slots: private slots:
void grab(); void grab();
@@ -89,10 +101,10 @@ private:
screen_window_t m_window; screen_window_t m_window;
screen_context_t m_screenContext; screen_context_t m_screenContext;
screen_pixmap_t m_screenPixmap; screen_pixmap_t m_screenPixmaps[2];
screen_buffer_t m_screenPixmapBuffer; screen_buffer_t m_screenPixmapBuffers[2];
char* m_screenBuffer; char *m_screenBuffers[2];
int m_screenBufferWidth; int m_screenBufferWidth;
int m_screenBufferHeight; int m_screenBufferHeight;
@@ -100,8 +112,13 @@ private:
bool m_active : 1; bool m_active : 1;
bool m_screenContextInitialized : 1; bool m_screenContextInitialized : 1;
bool m_screenPixmapInitialized : 1; bool m_screenPixmapBuffersInitialized : 1;
bool m_screenPixmapBufferInitialized : 1; int m_currentFrame;
EGLImageKHR img[2];
GLuint imgTextures[2];
bool m_eglImageSupported : 1;
bool m_eglImagesInitialized : 1;
bool m_eglImageCheck : 1; // We must not send a grabed frame before this is true
}; };
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@@ -351,10 +351,11 @@ void MmRendererMediaPlayerControl::setState(QMediaPlayer::State state)
{ {
if (m_state != state) { if (m_state != state) {
if (m_videoRendererControl) { if (m_videoRendererControl) {
if (state == QMediaPlayer::PausedState) if (state == QMediaPlayer::PausedState || state == QMediaPlayer::StoppedState) {
m_videoRendererControl->pause(); m_videoRendererControl->pause();
else if ((state == QMediaPlayer::PlayingState) } else if ((state == QMediaPlayer::PlayingState)
&& (m_state == QMediaPlayer::PausedState)) { && (m_state == QMediaPlayer::PausedState
|| m_state == QMediaPlayer::StoppedState)) {
m_videoRendererControl->resume(); m_videoRendererControl->resume();
} }
} }

View File

@@ -46,6 +46,7 @@
#include <QCoreApplication> #include <QCoreApplication>
#include <QDebug> #include <QDebug>
#include <QVideoSurfaceFormat> #include <QVideoSurfaceFormat>
#include <QOpenGLContext>
#include <mm/renderer.h> #include <mm/renderer.h>
@@ -59,7 +60,7 @@ MmRendererPlayerVideoRendererControl::MmRendererPlayerVideoRendererControl(QObje
, m_context(0) , m_context(0)
, m_videoId(-1) , m_videoId(-1)
{ {
connect(m_windowGrabber, SIGNAL(frameGrabbed(QImage)), SLOT(frameGrabbed(QImage))); connect(m_windowGrabber, SIGNAL(frameGrabbed(QImage, int)), SLOT(frameGrabbed(QImage, int)));
} }
MmRendererPlayerVideoRendererControl::~MmRendererPlayerVideoRendererControl() MmRendererPlayerVideoRendererControl::~MmRendererPlayerVideoRendererControl()
@@ -75,6 +76,10 @@ QAbstractVideoSurface *MmRendererPlayerVideoRendererControl::surface() const
void MmRendererPlayerVideoRendererControl::setSurface(QAbstractVideoSurface *surface) void MmRendererPlayerVideoRendererControl::setSurface(QAbstractVideoSurface *surface)
{ {
m_surface = QPointer<QAbstractVideoSurface>(surface); m_surface = QPointer<QAbstractVideoSurface>(surface);
if (QOpenGLContext::currentContext())
m_windowGrabber->checkForEglImageExtension();
else
m_surface->setProperty("_q_GLThreadCallback", QVariant::fromValue<QObject*>(this));
} }
void MmRendererPlayerVideoRendererControl::attachDisplay(mmr_context_t *context) void MmRendererPlayerVideoRendererControl::attachDisplay(mmr_context_t *context)
@@ -139,20 +144,86 @@ void MmRendererPlayerVideoRendererControl::resume()
m_windowGrabber->resume(); m_windowGrabber->resume();
} }
void MmRendererPlayerVideoRendererControl::frameGrabbed(const QImage &frame) class BBTextureBuffer : public QAbstractVideoBuffer
{
public:
BBTextureBuffer(int handle) :
QAbstractVideoBuffer(QAbstractVideoBuffer::GLTextureHandle)
{
m_handle = handle;
}
MapMode mapMode() const {
return QAbstractVideoBuffer::ReadWrite;
}
void unmap() {
}
uchar *map(MapMode mode, int * numBytes, int * bytesPerLine) {
Q_UNUSED(mode);
Q_UNUSED(numBytes);
Q_UNUSED(bytesPerLine);
return 0;
}
QVariant handle() const {
return m_handle;
}
private:
int m_handle;
};
void MmRendererPlayerVideoRendererControl::frameGrabbed(const QImage &frame, int handle)
{ {
if (m_surface) { if (m_surface) {
if (!m_surface->isActive()) { if (!m_surface->isActive()) {
m_surface->start(QVideoSurfaceFormat(frame.size(), QVideoFrame::Format_ARGB32)); if (m_windowGrabber->eglImageSupported()) {
if (QOpenGLContext::currentContext())
m_windowGrabber->createEglImages();
else
m_surface->setProperty("_q_GLThreadCallback", QVariant::fromValue<QObject*>(this));
m_surface->start(QVideoSurfaceFormat(frame.size(), QVideoFrame::Format_BGR32,
QAbstractVideoBuffer::GLTextureHandle));
} else {
m_surface->start(QVideoSurfaceFormat(frame.size(), QVideoFrame::Format_ARGB32));
}
} else { } else {
if (m_surface->surfaceFormat().frameSize() != frame.size()) { if (m_surface->surfaceFormat().frameSize() != frame.size()) {
QAbstractVideoBuffer::HandleType type = m_surface->surfaceFormat().handleType();
m_surface->stop(); m_surface->stop();
m_surface->start(QVideoSurfaceFormat(frame.size(), QVideoFrame::Format_ARGB32)); if (type != QAbstractVideoBuffer::NoHandle) {
m_surface->setProperty("_q_GLThreadCallback", QVariant::fromValue<QObject*>(this));
m_surface->start(QVideoSurfaceFormat(frame.size(), QVideoFrame::Format_BGR32,
QAbstractVideoBuffer::GLTextureHandle));
} else {
m_surface->start(QVideoSurfaceFormat(frame.size(), QVideoFrame::Format_ARGB32));
}
} }
} }
m_surface->present(frame.copy()); // Depending on the support of EGL images on the current platform we either pass a texture
// handle or a copy of the image data
if (m_surface->surfaceFormat().handleType() != QAbstractVideoBuffer::NoHandle) {
if (m_windowGrabber->eglImagesInitialized() &&
m_surface->property("_q_GLThreadCallback") != 0)
m_surface->setProperty("_q_GLThreadCallback", 0);
BBTextureBuffer *textBuffer = new BBTextureBuffer(handle);
QVideoFrame actualFrame(textBuffer, frame.size(), QVideoFrame::Format_BGR32);
m_surface->present(actualFrame);
} else {
m_surface->present(frame.copy());
}
} }
} }
void MmRendererPlayerVideoRendererControl::customEvent(QEvent *e)
{
// This is running in the render thread (OpenGL enabled)
if (e->type() == QEvent::User)
m_windowGrabber->checkForEglImageExtension();
else if (e->type() == QEvent::User + 1)
m_windowGrabber->createEglImages();
}
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@@ -67,8 +67,10 @@ public:
void pause(); void pause();
void resume(); void resume();
void customEvent(QEvent *) Q_DECL_OVERRIDE;
private Q_SLOTS: private Q_SLOTS:
void frameGrabbed(const QImage &frame); void frameGrabbed(const QImage &frame, int);
private: private:
QPointer<QAbstractVideoSurface> m_surface; QPointer<QAbstractVideoSurface> m_surface;

View File

@@ -2,6 +2,7 @@ TARGET = imx6vivantevideonode
QT += multimedia-private qtmultimediaquicktools-private QT += multimedia-private qtmultimediaquicktools-private
PLUGIN_TYPE=video/videonode PLUGIN_TYPE=video/videonode
PLUGIN_EXTENDS = quick
PLUGIN_CLASS_NAME = QSGVivanteVideoNodeFactory PLUGIN_CLASS_NAME = QSGVivanteVideoNodeFactory
load(qt_plugin) load(qt_plugin)

View File

@@ -85,5 +85,5 @@ ULONG MFAbstractActivate::Release(void)
ULONG cRef = InterlockedDecrement(&m_cRef); ULONG cRef = InterlockedDecrement(&m_cRef);
if (cRef == 0) if (cRef == 0)
delete this; delete this;
return m_cRef; return cRef;
} }

View File

@@ -1157,8 +1157,8 @@ void MFPlayerSession::setPositionInternal(qint64 position, Command requestCmd)
qreal MFPlayerSession::playbackRate() const qreal MFPlayerSession::playbackRate() const
{ {
if (m_pendingState != NoPending) if (m_scrubbing)
return m_request.rate; return m_restoreRate;
return m_state.rate; return m_state.rate;
} }
@@ -1166,6 +1166,7 @@ void MFPlayerSession::setPlaybackRate(qreal rate)
{ {
if (m_scrubbing) { if (m_scrubbing) {
m_restoreRate = rate; m_restoreRate = rate;
emit playbackRateChanged(rate);
return; return;
} }
setPlaybackRateInternal(rate); setPlaybackRateInternal(rate);
@@ -1194,6 +1195,8 @@ void MFPlayerSession::setPlaybackRateInternal(qreal rate)
isThin = TRUE; isThin = TRUE;
if (FAILED(m_rateSupport->IsRateSupported(isThin, rate, NULL))) { if (FAILED(m_rateSupport->IsRateSupported(isThin, rate, NULL))) {
qWarning() << "unable to set playbackrate = " << rate; qWarning() << "unable to set playbackrate = " << rate;
m_pendingRate = m_request.rate = m_state.rate;
return;
} }
} }
if (m_pendingState != NoPending) { if (m_pendingState != NoPending) {
@@ -1219,6 +1222,7 @@ void MFPlayerSession::commitRateChange(qreal rate, BOOL isThin)
MFTIME hnsSystemTime = 0; MFTIME hnsSystemTime = 0;
MFTIME hnsClockTime = 0; MFTIME hnsClockTime = 0;
Command cmdNow = m_state.command; Command cmdNow = m_state.command;
bool resetPosition = false;
// Allowed rate transitions: // Allowed rate transitions:
// Positive <-> negative: Stopped // Positive <-> negative: Stopped
// Negative <-> zero: Stopped // Negative <-> zero: Stopped
@@ -1229,7 +1233,12 @@ void MFPlayerSession::commitRateChange(qreal rate, BOOL isThin)
m_presentationClock->GetCorrelatedTime(0, &hnsClockTime, &hnsSystemTime); m_presentationClock->GetCorrelatedTime(0, &hnsClockTime, &hnsSystemTime);
Q_ASSERT(hnsSystemTime != 0); Q_ASSERT(hnsSystemTime != 0);
m_request.setCommand(rate < 0 || m_state.rate < 0 ? CmdSeekResume : CmdStart); if (rate < 0 || m_state.rate < 0)
m_request.setCommand(CmdSeekResume);
else if (isThin || m_state.isThin)
m_request.setCommand(CmdStartAndSeek);
else
m_request.setCommand(CmdStart);
// We need to stop only when dealing with negative rates // We need to stop only when dealing with negative rates
if (rate >= 0 && m_state.rate >= 0) if (rate >= 0 && m_state.rate >= 0)
@@ -1247,7 +1256,9 @@ void MFPlayerSession::commitRateChange(qreal rate, BOOL isThin)
// session cannot transition back from stopped to paused. // session cannot transition back from stopped to paused.
// Therefore, this rate transition is not supported while paused. // Therefore, this rate transition is not supported while paused.
qWarning() << "Unable to change rate from positive to negative or vice versa in paused state"; qWarning() << "Unable to change rate from positive to negative or vice versa in paused state";
return; rate = m_state.rate;
isThin = m_state.isThin;
goto done;
} }
// This happens when resuming playback after scrubbing in pause mode. // This happens when resuming playback after scrubbing in pause mode.
@@ -1279,17 +1290,42 @@ void MFPlayerSession::commitRateChange(qreal rate, BOOL isThin)
// Resume to the current position (stop() will reset the position to 0) // Resume to the current position (stop() will reset the position to 0)
m_request.start = hnsClockTime / 10000; m_request.start = hnsClockTime / 10000;
} else if (!isThin && m_state.isThin) {
if (cmdNow == CmdStart) {
// When thinning, only key frames are read and presented. Going back
// to normal playback requires to reset the current position to force
// the pipeline to decode the actual frame at the current position
// (which might be earlier than the last decoded key frame)
resetPosition = true;
} else if (cmdNow == CmdPause) {
// If paused, dont reset the position until we resume, otherwise
// a new frame will be rendered
m_presentationClock->GetCorrelatedTime(0, &hnsClockTime, &hnsSystemTime);
m_request.setCommand(CmdSeekResume);
m_request.start = hnsClockTime / 10000;
}
} }
// Set the rate. // Set the rate.
if (FAILED(m_rateControl->SetRate(isThin, rate))) { if (FAILED(m_rateControl->SetRate(isThin, rate))) {
qWarning() << "failed to set playbackrate = " << rate; qWarning() << "failed to set playbackrate = " << rate;
return; rate = m_state.rate;
isThin = m_state.isThin;
goto done;
} }
if (resetPosition) {
m_presentationClock->GetCorrelatedTime(0, &hnsClockTime, &hnsSystemTime);
setPosition(hnsClockTime / 10000);
}
done:
// Adjust our current rate and requested rate. // Adjust our current rate and requested rate.
m_pendingRate = m_request.rate = m_state.rate = rate; m_pendingRate = m_request.rate = m_state.rate = rate;
if (rate != 0)
m_state.isThin = isThin;
emit playbackRateChanged(rate);
} }
void MFPlayerSession::scrub(bool enableScrub) void MFPlayerSession::scrub(bool enableScrub)
@@ -1705,6 +1741,11 @@ void MFPlayerSession::updatePendingCommands(Command command)
case CmdSeek: case CmdSeek:
case CmdSeekResume: case CmdSeekResume:
setPositionInternal(m_request.start, m_request.command); setPositionInternal(m_request.start, m_request.command);
break;
case CmdStartAndSeek:
start();
setPositionInternal(m_request.start, m_request.command);
break;
} }
m_request.setCommand(CmdNone); m_request.setCommand(CmdNone);
} }

View File

@@ -165,6 +165,7 @@ private:
CmdPause, CmdPause,
CmdSeek, CmdSeek,
CmdSeekResume, CmdSeekResume,
CmdStartAndSeek
}; };
void clear(); void clear();

View File

@@ -655,11 +655,6 @@ namespace
m_presentationClock = NULL; m_presentationClock = NULL;
} }
if (m_scheduledBuffer) {
m_scheduledBuffer->Release();
m_scheduledBuffer = NULL;
}
clearMediaTypes(); clearMediaTypes();
clearSampleQueue(); clearSampleQueue();
clearBufferCache(); clearBufferCache();
@@ -677,6 +672,7 @@ namespace
QMutexLocker locker(&m_mutex); QMutexLocker locker(&m_mutex);
HRESULT hr = validateOperation(OpPreroll); HRESULT hr = validateOperation(OpPreroll);
if (SUCCEEDED(hr)) { if (SUCCEEDED(hr)) {
m_state = State_Prerolling;
m_prerollTargetTime = hnsUpcomingStartTime; m_prerollTargetTime = hnsUpcomingStartTime;
hr = queueAsyncOperation(OpPreroll); hr = queueAsyncOperation(OpPreroll);
} }
@@ -772,9 +768,12 @@ namespace
qDebug() << "MediaStream::setRate" << rate; qDebug() << "MediaStream::setRate" << rate;
#endif #endif
QMutexLocker locker(&m_mutex); QMutexLocker locker(&m_mutex);
m_rate = rate; HRESULT hr = validateOperation(OpSetRate);
queueEvent(MEStreamSinkRateChanged, GUID_NULL, S_OK, NULL); if (SUCCEEDED(hr)) {
return S_OK; m_rate = rate;
hr = queueAsyncOperation(OpSetRate);
}
return hr;
} }
void supportedFormatsChanged() void supportedFormatsChanged()
@@ -861,6 +860,7 @@ namespace
if (m_scheduledBuffer) { if (m_scheduledBuffer) {
m_scheduledBuffer->Release(); m_scheduledBuffer->Release();
m_scheduledBuffer = NULL; m_scheduledBuffer = NULL;
schedulePresentation(true);
} }
} }
@@ -933,6 +933,7 @@ namespace
{ {
State_TypeNotSet = 0, // No media type is set State_TypeNotSet = 0, // No media type is set
State_Ready, // Media type is set, Start has never been called. State_Ready, // Media type is set, Start has never been called.
State_Prerolling,
State_Started, State_Started,
State_Paused, State_Paused,
State_Stopped, State_Stopped,
@@ -1121,6 +1122,9 @@ namespace
break; break;
} }
} }
if (m_state == State_Started)
schedulePresentation(true);
case OpRestart: case OpRestart:
endPreroll(S_FALSE); endPreroll(S_FALSE);
if (SUCCEEDED(hr)) { if (SUCCEEDED(hr)) {
@@ -1139,10 +1143,7 @@ namespace
case OpStop: case OpStop:
// Drop samples from queue. // Drop samples from queue.
hr = processSamplesFromQueue(DropSamples); hr = processSamplesFromQueue(DropSamples);
if (m_scheduledBuffer) { clearBufferCache();
m_scheduledBuffer->Release();
m_scheduledBuffer = NULL;
}
// Send the event even if the previous call failed. // Send the event even if the previous call failed.
hr = queueEvent(MEStreamSinkStopped, GUID_NULL, hr, NULL); hr = queueEvent(MEStreamSinkStopped, GUID_NULL, hr, NULL);
if (m_surface->isActive()) { if (m_surface->isActive()) {
@@ -1158,7 +1159,7 @@ namespace
hr = queueEvent(MEStreamSinkPaused, GUID_NULL, hr, NULL); hr = queueEvent(MEStreamSinkPaused, GUID_NULL, hr, NULL);
break; break;
case OpSetRate: case OpSetRate:
//TODO: hr = queueEvent(MEStreamSinkRateChanged, GUID_NULL, S_OK, NULL);
break; break;
case OpProcessSample: case OpProcessSample:
case OpPlaceMarker: case OpPlaceMarker:
@@ -1335,7 +1336,7 @@ namespace
pSample->GetSampleDuration(&duration); pSample->GetSampleDuration(&duration);
if (m_prerolling) { if (m_prerolling) {
if (SUCCEEDED(hr) && time >= m_prerollTargetTime) { if (SUCCEEDED(hr) && ((time - m_prerollTargetTime) * m_rate) >= 0) {
IMFMediaBuffer *pBuffer = NULL; IMFMediaBuffer *pBuffer = NULL;
hr = pSample->ConvertToContiguousBuffer(&pBuffer); hr = pSample->ConvertToContiguousBuffer(&pBuffer);
if (SUCCEEDED(hr)) { if (SUCCEEDED(hr)) {
@@ -1352,7 +1353,7 @@ namespace
} else { } else {
bool requestSample = true; bool requestSample = true;
// If the time stamp is too early, just discard this sample. // If the time stamp is too early, just discard this sample.
if (SUCCEEDED(hr) && time >= m_startTime) { if (SUCCEEDED(hr) && ((time - m_startTime) * m_rate) >= 0) {
IMFMediaBuffer *pBuffer = NULL; IMFMediaBuffer *pBuffer = NULL;
hr = pSample->ConvertToContiguousBuffer(&pBuffer); hr = pSample->ConvertToContiguousBuffer(&pBuffer);
if (SUCCEEDED(hr)) { if (SUCCEEDED(hr)) {
@@ -1385,11 +1386,16 @@ namespace
foreach (SampleBuffer sb, m_bufferCache) foreach (SampleBuffer sb, m_bufferCache)
sb.m_buffer->Release(); sb.m_buffer->Release();
m_bufferCache.clear(); m_bufferCache.clear();
if (m_scheduledBuffer) {
m_scheduledBuffer->Release();
m_scheduledBuffer = NULL;
}
} }
void schedulePresentation(bool requestSample) void schedulePresentation(bool requestSample)
{ {
if (m_state == State_Paused) if (m_state == State_Paused || m_state == State_Prerolling)
return; return;
if (!m_scheduledBuffer) { if (!m_scheduledBuffer) {
//get time from presentation time //get time from presentation time
@@ -1400,9 +1406,8 @@ namespace
timeOK = false; timeOK = false;
} }
while (!m_bufferCache.isEmpty()) { while (!m_bufferCache.isEmpty()) {
SampleBuffer sb = m_bufferCache.first(); SampleBuffer sb = m_bufferCache.takeFirst();
m_bufferCache.pop_front(); if (timeOK && ((sb.m_time - currentTime) * m_rate) < 0) {
if (timeOK && currentTime > sb.m_time) {
sb.m_buffer->Release(); sb.m_buffer->Release();
#ifdef DEBUG_MEDIAFOUNDATION #ifdef DEBUG_MEDIAFOUNDATION
qDebug() << "currentPresentTime =" << float(currentTime / 10000) * 0.001f << " and sampleTime is" << float(sb.m_time / 10000) * 0.001f; qDebug() << "currentPresentTime =" << float(currentTime / 10000) * 0.001f << " and sampleTime is" << float(sb.m_time / 10000) * 0.001f;
@@ -1439,6 +1444,8 @@ namespace
/* Ready */ TRUE, TRUE, TRUE, FALSE, TRUE, TRUE, TRUE, FALSE, TRUE, TRUE, /* Ready */ TRUE, TRUE, TRUE, FALSE, TRUE, TRUE, TRUE, FALSE, TRUE, TRUE,
/* Prerolling */ TRUE, TRUE, FALSE, FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE,
/* Start */ FALSE, TRUE, TRUE, FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, /* Start */ FALSE, TRUE, TRUE, FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE,
/* Pause */ FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, /* Pause */ FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE,
@@ -1454,7 +1461,11 @@ namespace
// 2. While paused, the sink accepts samples but does not process them. // 2. While paused, the sink accepts samples but does not process them.
}; };
class MediaSink : public IMFFinalizableMediaSink, public IMFClockStateSink, public IMFMediaSinkPreroll class MediaSink : public IMFFinalizableMediaSink,
public IMFClockStateSink,
public IMFMediaSinkPreroll,
public IMFGetService,
public IMFRateSupport
{ {
public: public:
MediaSink(MFVideoRendererControl *rendererControl) MediaSink(MFVideoRendererControl *rendererControl)
@@ -1526,10 +1537,14 @@ namespace
return E_POINTER; return E_POINTER;
if (riid == IID_IMFMediaSink) { if (riid == IID_IMFMediaSink) {
*ppvObject = static_cast<IMFMediaSink*>(this); *ppvObject = static_cast<IMFMediaSink*>(this);
} else if (riid == IID_IMFGetService) {
*ppvObject = static_cast<IMFGetService*>(this);
} else if (riid == IID_IMFMediaSinkPreroll) { } else if (riid == IID_IMFMediaSinkPreroll) {
*ppvObject = static_cast<IMFMediaSinkPreroll*>(this); *ppvObject = static_cast<IMFMediaSinkPreroll*>(this);
} else if (riid == IID_IMFClockStateSink) { } else if (riid == IID_IMFClockStateSink) {
*ppvObject = static_cast<IMFClockStateSink*>(this); *ppvObject = static_cast<IMFClockStateSink*>(this);
} else if (riid == IID_IMFRateSupport) {
*ppvObject = static_cast<IMFRateSupport*>(this);
} else if (riid == IID_IUnknown) { } else if (riid == IID_IUnknown) {
*ppvObject = static_cast<IUnknown*>(static_cast<IMFFinalizableMediaSink*>(this)); *ppvObject = static_cast<IUnknown*>(static_cast<IMFFinalizableMediaSink*>(this));
} else { } else {
@@ -1554,7 +1569,19 @@ namespace
return cRef; return cRef;
} }
// IMFGetService methods
STDMETHODIMP GetService(const GUID &guidService,
const IID &riid,
LPVOID *ppvObject)
{
if (!ppvObject)
return E_POINTER;
if (guidService != MF_RATE_CONTROL_SERVICE)
return MF_E_UNSUPPORTED_SERVICE;
return QueryInterface(riid, ppvObject);
}
//IMFMediaSinkPreroll //IMFMediaSinkPreroll
STDMETHODIMP NotifyPreroll(MFTIME hnsUpcomingStartTime) STDMETHODIMP NotifyPreroll(MFTIME hnsUpcomingStartTime)
@@ -1749,6 +1776,68 @@ namespace
return m_stream->setRate(flRate); return m_stream->setRate(flRate);
} }
// IMFRateSupport methods
STDMETHODIMP GetFastestRate(MFRATE_DIRECTION eDirection,
BOOL fThin,
float *pflRate)
{
if (!pflRate)
return E_POINTER;
*pflRate = (fThin ? 8.f : 2.0f) * (eDirection == MFRATE_FORWARD ? 1 : -1) ;
return S_OK;
}
STDMETHODIMP GetSlowestRate(MFRATE_DIRECTION eDirection,
BOOL fThin,
float *pflRate)
{
Q_UNUSED(eDirection);
Q_UNUSED(fThin);
if (!pflRate)
return E_POINTER;
// we support any rate
*pflRate = 0.f;
return S_OK;
}
STDMETHODIMP IsRateSupported(BOOL fThin,
float flRate,
float *pflNearestSupportedRate)
{
HRESULT hr = S_OK;
if (!qFuzzyIsNull(flRate)) {
MFRATE_DIRECTION direction = flRate > 0.f ? MFRATE_FORWARD
: MFRATE_REVERSE;
float fastestRate = 0.f;
float slowestRate = 0.f;
GetFastestRate(direction, fThin, &fastestRate);
GetSlowestRate(direction, fThin, &slowestRate);
if (direction == MFRATE_REVERSE)
qSwap(fastestRate, slowestRate);
if (flRate < slowestRate || flRate > fastestRate) {
hr = MF_E_UNSUPPORTED_RATE;
if (pflNearestSupportedRate) {
*pflNearestSupportedRate = qBound(slowestRate,
flRate,
fastestRate);
}
}
} else if (pflNearestSupportedRate) {
*pflNearestSupportedRate = flRate;
}
return hr;
}
private: private:
long m_cRef; long m_cRef;
QMutex m_mutex; QMutex m_mutex;
@@ -2201,13 +2290,13 @@ void MFVideoRendererControl::customEvent(QEvent *event)
MFTIME targetTime = static_cast<MediaStream::PresentEvent*>(event)->targetTime(); MFTIME targetTime = static_cast<MediaStream::PresentEvent*>(event)->targetTime();
MFTIME currentTime = static_cast<VideoRendererActivate*>(m_currentActivate)->getTime(); MFTIME currentTime = static_cast<VideoRendererActivate*>(m_currentActivate)->getTime();
float playRate = static_cast<VideoRendererActivate*>(m_currentActivate)->getPlayRate(); float playRate = static_cast<VideoRendererActivate*>(m_currentActivate)->getPlayRate();
if (!qFuzzyIsNull(playRate)) { if (!qFuzzyIsNull(playRate) && targetTime != currentTime) {
// If the scheduled frame is too late or too much in advance, skip it // If the scheduled frame is too late, skip it
const int diff = (targetTime - currentTime) / 10000; const int interval = ((targetTime - currentTime) / 10000) / playRate;
if (diff < 0 || diff > 500) if (interval < 0)
static_cast<VideoRendererActivate*>(m_currentActivate)->clearScheduledFrame(); static_cast<VideoRendererActivate*>(m_currentActivate)->clearScheduledFrame();
else else
QTimer::singleShot(diff / playRate, this, SLOT(present())); QTimer::singleShot(interval, this, SLOT(present()));
} else { } else {
present(); present();
} }

View File

@@ -72,6 +72,9 @@ public:
protected: protected:
SampleGrabberCallback() : m_cRef(1) {} SampleGrabberCallback() : m_cRef(1) {}
public:
virtual ~SampleGrabberCallback() {}
private: private:
long m_cRef; long m_cRef;
}; };

View File

@@ -278,7 +278,7 @@ IMFMediaSource* SourceResolver::mediaSource() const
///////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////
SourceResolver::State::State(IMFSourceResolver *sourceResolver, bool fromStream) SourceResolver::State::State(IMFSourceResolver *sourceResolver, bool fromStream)
: m_cRef(1) : m_cRef(0)
, m_sourceResolver(sourceResolver) , m_sourceResolver(sourceResolver)
, m_fromStream(fromStream) , m_fromStream(fromStream)
{ {

View File

@@ -201,6 +201,16 @@ QSGNode *QDeclarativeVideoRendererBackend::updatePaintNode(QSGNode *oldNode,
obj->event(&ev); obj->event(&ev);
} }
} }
#if defined (Q_OS_QNX) // On QNX we need to be called back again for creating the egl images
else {
// Internal mechanism to call back the surface renderer from the QtQuick render thread
QObject *obj = m_surface->property("_q_GLThreadCallback").value<QObject*>();
if (obj) {
QEvent ev(static_cast<QEvent::Type>(QEvent::User + 1));
obj->event(&ev);
}
}
#endif
if (m_frameChanged) { if (m_frameChanged) {
if (videoNode && videoNode->pixelFormat() != m_frame.pixelFormat()) { if (videoNode && videoNode->pixelFormat() != m_frame.pixelFormat()) {

View File

@@ -46,6 +46,7 @@ QT_BEGIN_NAMESPACE
QSGVideoNode::QSGVideoNode() QSGVideoNode::QSGVideoNode()
: m_orientation(-1) : m_orientation(-1)
{ {
setFlag(QSGNode::OwnsGeometry);
} }
/* Helpers */ /* Helpers */

View File

@@ -777,7 +777,7 @@ void tst_QDeclarativeAudio::playbackRate()
audio.setPlaybackRate(2.0); audio.setPlaybackRate(2.0);
QCOMPARE(audio.playbackRate(), qreal(2.0)); QCOMPARE(audio.playbackRate(), qreal(2.0));
QCOMPARE(provider.playerControl()->playbackRate(), qreal(2.0)); QCOMPARE(provider.playerControl()->playbackRate(), qreal(2.0));
QCOMPARE(spy.count(), 3); QCOMPARE(spy.count(), 2);
} }
void tst_QDeclarativeAudio::status() void tst_QDeclarativeAudio::status()

View File

@@ -413,8 +413,7 @@ void tst_QMediaPlaylistNavigator::testCurrentIndexChangedSignal()
{ {
QMediaNetworkPlaylistProvider playlist; QMediaNetworkPlaylistProvider playlist;
QMediaPlaylistNavigator navigator(&playlist); QMediaPlaylistNavigator navigator(&playlist);
navigator.setPlaybackMode(QMediaPlaylist::Random); QCOMPARE(navigator.playbackMode(), QMediaPlaylist::Sequential);
QCOMPARE(navigator.playbackMode(), QMediaPlaylist::Random);
QCOMPARE(navigator.currentIndex(), -1); QCOMPARE(navigator.currentIndex(), -1);
//Creating a QSignalSpy object for currentIndexChanged() signal //Creating a QSignalSpy object for currentIndexChanged() signal

View File

@@ -907,7 +907,7 @@ void tst_QVideoWidget::fullScreenWindowControl()
QtTestVideoObject object(new QtTestWindowControl, 0, 0); QtTestVideoObject object(new QtTestWindowControl, 0, 0);
QVideoWidget widget; QVideoWidget widget;
object.bind(&widget); object.bind(&widget);
widget.show(); widget.showNormal();
QVERIFY(QTest::qWaitForWindowExposed(&widget)); QVERIFY(QTest::qWaitForWindowExposed(&widget));
Qt::WindowFlags windowFlags = widget.windowFlags(); Qt::WindowFlags windowFlags = widget.windowFlags();
@@ -992,7 +992,7 @@ void tst_QVideoWidget::fullScreenWidgetControl()
QtTestVideoObject object(0, new QtTestWidgetControl, 0); QtTestVideoObject object(0, new QtTestWidgetControl, 0);
QVideoWidget widget; QVideoWidget widget;
object.bind(&widget); object.bind(&widget);
widget.show(); widget.showNormal();
QVERIFY(QTest::qWaitForWindowExposed(&widget)); QVERIFY(QTest::qWaitForWindowExposed(&widget));
Qt::WindowFlags windowFlags = widget.windowFlags(); Qt::WindowFlags windowFlags = widget.windowFlags();
@@ -1078,7 +1078,7 @@ void tst_QVideoWidget::fullScreenRendererControl()
QtTestVideoObject object(0, 0, new QtTestRendererControl); QtTestVideoObject object(0, 0, new QtTestRendererControl);
QVideoWidget widget; QVideoWidget widget;
object.bind(&widget); object.bind(&widget);
widget.show(); widget.showNormal();
QVERIFY(QTest::qWaitForWindowExposed(&widget)); QVERIFY(QTest::qWaitForWindowExposed(&widget));
Qt::WindowFlags windowFlags = widget.windowFlags(); Qt::WindowFlags windowFlags = widget.windowFlags();