summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorYoann Lopes <yoann.lopes@digia.com>2013-07-05 16:26:55 +0200
committerThe Qt Project <gerrit-noreply@qt-project.org>2013-07-09 09:41:32 +0200
commit5e7e8e04d1be82e23f69e2966d355b4aa8d93442 (patch)
treea497a943e7cb833bff339b4b448f06c054226c1b /src
parent1dfbe44d90fb0a13f67642ca61a64c91f8322e9f (diff)
Android: wait to have a valid video surface before loading a media.
Setting the video surface on the Android media player after it has loaded the media doesn't work on some hardware. Change-Id: I5e621a34ace9de458bfc65bfac8fa50c29cee9a5 Reviewed-by: Christian Stromme <christian.stromme@digia.com>
Diffstat (limited to 'src')
-rw-r--r--src/plugins/android/mediaplayer/qandroidmediaplayercontrol.cpp54
-rw-r--r--src/plugins/android/mediaplayer/qandroidmediaplayercontrol.h4
-rw-r--r--src/plugins/android/mediaplayer/qandroidvideooutput.h6
-rw-r--r--src/plugins/android/mediaplayer/qandroidvideorendercontrol.cpp84
-rw-r--r--src/plugins/android/mediaplayer/qandroidvideorendercontrol.h9
5 files changed, 130 insertions, 27 deletions
diff --git a/src/plugins/android/mediaplayer/qandroidmediaplayercontrol.cpp b/src/plugins/android/mediaplayer/qandroidmediaplayercontrol.cpp
index cb34fba32..a70f4e130 100644
--- a/src/plugins/android/mediaplayer/qandroidmediaplayercontrol.cpp
+++ b/src/plugins/android/mediaplayer/qandroidmediaplayercontrol.cpp
@@ -45,6 +45,12 @@
QT_BEGIN_NAMESPACE
+static void textureReadyCallback(void *context)
+{
+ if (context)
+ reinterpret_cast<QAndroidMediaPlayerControl *>(context)->onSurfaceTextureReady();
+}
+
QAndroidMediaPlayerControl::QAndroidMediaPlayerControl(QObject *parent)
: QMediaPlayerControl(parent),
mMediaPlayer(new JMediaPlayer),
@@ -58,7 +64,8 @@ QAndroidMediaPlayerControl::QAndroidMediaPlayerControl(QObject *parent)
mVideoAvailable(false),
mBuffering(false),
mMediaPlayerReady(false),
- mPendingPosition(-1)
+ mPendingPosition(-1),
+ mPendingSetMedia(false)
{
connect(mMediaPlayer, SIGNAL(bufferingUpdate(qint32)),
this, SLOT(onBufferChanged(qint32)));
@@ -208,6 +215,13 @@ void QAndroidMediaPlayerControl::setMedia(const QMediaContent &mediaContent,
mMediaContent = mediaContent;
mMediaStream = stream;
+ if (mVideoOutput && !mMediaPlayer->display()) {
+ // if a video output is set but the video texture is not ready, delay loading the media
+ // since it can cause problems on some hardware
+ mPendingSetMedia = true;
+ return;
+ }
+
const QString uri = mediaContent.canonicalUrl().toString();
if (!uri.isEmpty())
@@ -231,6 +245,13 @@ void QAndroidMediaPlayerControl::setVideoOutput(QAndroidVideoOutput *videoOutput
mVideoOutput->stop();
mVideoOutput = videoOutput;
+
+ if (mVideoOutput && !mMediaPlayer->display()) {
+ if (mVideoOutput->isTextureReady())
+ mMediaPlayer->setDisplay(mVideoOutput->surfaceHolder());
+ else
+ mVideoOutput->setTextureReadyCallback(textureReadyCallback, this);
+ }
}
void QAndroidMediaPlayerControl::play()
@@ -239,7 +260,8 @@ void QAndroidMediaPlayerControl::play()
mPendingState = QMediaPlayer::PlayingState;
if (mCurrentState == QMediaPlayer::StoppedState
&& !mMediaContent.isNull()
- && mCurrentMediaStatus != QMediaPlayer::LoadingMedia) {
+ && mCurrentMediaStatus != QMediaPlayer::LoadingMedia
+ && !mPendingSetMedia) {
setMedia(mMediaContent, 0);
}
return;
@@ -392,16 +414,23 @@ void QAndroidMediaPlayerControl::onBufferChanged(qint32 percent)
void QAndroidMediaPlayerControl::onVideoSizeChanged(qint32 width, qint32 height)
{
- if (width == 0 || height == 0)
+ QSize newSize(width, height);
+
+ if (width == 0 || height == 0 || newSize == mVideoSize)
return;
setVideoAvailable(true);
+ mVideoSize = newSize;
- if (mVideoOutput) {
- if (!mMediaPlayer->display())
- mMediaPlayer->setDisplay(mVideoOutput->surfaceHolder());
- if (mMediaPlayer->display())
- mVideoOutput->setVideoSize(QSize(width, height));
+ if (mVideoOutput)
+ mVideoOutput->setVideoSize(mVideoSize);
+}
+
+void QAndroidMediaPlayerControl::onSurfaceTextureReady()
+{
+ if (!mMediaPlayer->display() && mVideoOutput) {
+ mMediaPlayer->setDisplay(mVideoOutput->surfaceHolder());
+ flushPendingStates();
}
}
@@ -465,6 +494,9 @@ void QAndroidMediaPlayerControl::setVideoAvailable(bool available)
if (mVideoAvailable == available)
return;
+ if (!available)
+ mVideoSize = QSize();
+
mVideoAvailable = available;
Q_EMIT videoAvailableChanged(mVideoAvailable);
}
@@ -479,6 +511,12 @@ void QAndroidMediaPlayerControl::resetBufferingProgress()
void QAndroidMediaPlayerControl::flushPendingStates()
{
+ if (mPendingSetMedia) {
+ setMedia(mMediaContent, 0);
+ mPendingSetMedia = false;
+ return;
+ }
+
switch (mPendingState) {
case QMediaPlayer::PlayingState:
if (mPendingPosition > -1)
diff --git a/src/plugins/android/mediaplayer/qandroidmediaplayercontrol.h b/src/plugins/android/mediaplayer/qandroidmediaplayercontrol.h
index 445e8de7a..93eced853 100644
--- a/src/plugins/android/mediaplayer/qandroidmediaplayercontrol.h
+++ b/src/plugins/android/mediaplayer/qandroidmediaplayercontrol.h
@@ -44,6 +44,7 @@
#include <qglobal.h>
#include <QMediaPlayerControl>
+#include <qsize.h>
QT_BEGIN_NAMESPACE
@@ -75,6 +76,7 @@ public:
void setMedia(const QMediaContent &mediaContent, QIODevice *stream) Q_DECL_OVERRIDE;
void setVideoOutput(QAndroidVideoOutput *videoOutput);
+ void onSurfaceTextureReady();
Q_SIGNALS:
void metaDataUpdated();
@@ -105,11 +107,13 @@ private:
int mBufferPercent;
bool mAudioAvailable;
bool mVideoAvailable;
+ QSize mVideoSize;
bool mBuffering;
QMediaTimeRange mAvailablePlaybackRange;
bool mMediaPlayerReady;
QMediaPlayer::State mPendingState;
qint64 mPendingPosition;
+ bool mPendingSetMedia;
void setState(QMediaPlayer::State state);
void setMediaStatus(QMediaPlayer::MediaStatus status);
diff --git a/src/plugins/android/mediaplayer/qandroidvideooutput.h b/src/plugins/android/mediaplayer/qandroidvideooutput.h
index 99db7c3e7..d59971f3b 100644
--- a/src/plugins/android/mediaplayer/qandroidvideooutput.h
+++ b/src/plugins/android/mediaplayer/qandroidvideooutput.h
@@ -48,6 +48,8 @@
QT_BEGIN_NAMESPACE
+typedef void (*TextureReadyCallback)(void*);
+
class QAndroidVideoOutput
{
public:
@@ -55,6 +57,10 @@ public:
virtual ~QAndroidVideoOutput() { }
virtual jobject surfaceHolder() = 0;
+
+ virtual bool isTextureReady() = 0;
+ virtual void setTextureReadyCallback(TextureReadyCallback cb, void *context = 0) = 0;
+
virtual void setVideoSize(const QSize &size) = 0;
virtual void stop() = 0;
};
diff --git a/src/plugins/android/mediaplayer/qandroidvideorendercontrol.cpp b/src/plugins/android/mediaplayer/qandroidvideorendercontrol.cpp
index c63e0e771..fe26b455a 100644
--- a/src/plugins/android/mediaplayer/qandroidvideorendercontrol.cpp
+++ b/src/plugins/android/mediaplayer/qandroidvideorendercontrol.cpp
@@ -50,6 +50,7 @@
#include <QVideoSurfaceFormat>
#include <QOpenGLFunctions>
#include <QOpenGLShaderProgram>
+#include <qevent.h>
QT_BEGIN_NAMESPACE
@@ -134,6 +135,8 @@ QAndroidVideoRendererControl::QAndroidVideoRendererControl(QObject *parent)
, m_surfaceTexture(0)
, m_surfaceHolder(0)
, m_externalTex(0)
+ , m_textureReadyCallback(0)
+ , m_textureReadyContext(0)
{
}
@@ -177,42 +180,66 @@ void QAndroidVideoRendererControl::setSurface(QAbstractVideoSurface *surface)
if (surface == m_surface)
return;
- if (m_surface && m_surface->isActive())
+ if (m_surface && m_surface->isActive()) {
m_surface->stop();
+ m_surface->removeEventFilter(this);
+ }
m_surface = surface;
- if (m_surface)
+ if (m_surface) {
m_useImage = !m_surface->supportedPixelFormats(QAbstractVideoBuffer::GLTextureHandle).contains(QVideoFrame::Format_BGR32);
+ m_surface->installEventFilter(this);
+ }
}
-jobject QAndroidVideoRendererControl::surfaceHolder()
+bool QAndroidVideoRendererControl::isTextureReady()
+{
+ return QOpenGLContext::currentContext() || (m_surface && m_surface->property("GLContext").isValid());
+}
+
+void QAndroidVideoRendererControl::setTextureReadyCallback(TextureReadyCallback cb, void *context)
+{
+ m_textureReadyCallback = cb;
+ m_textureReadyContext = context;
+}
+
+bool QAndroidVideoRendererControl::initSurfaceTexture()
{
- if (m_surfaceHolder)
- return m_surfaceHolder->object();
+ if (m_surfaceTexture)
+ return true;
+
+ if (!m_surface)
+ return false;
QOpenGLContext *currContext = QOpenGLContext::currentContext();
// If we don't have a GL context in the current thread, create one and share it
// with the render thread GL context
if (!currContext && !m_glContext) {
+ QOpenGLContext *shareContext = qobject_cast<QOpenGLContext*>(m_surface->property("GLContext").value<QObject*>());
+ if (!shareContext)
+ return false;
+
m_offscreenSurface = new QOffscreenSurface;
QSurfaceFormat format;
format.setSwapBehavior(QSurfaceFormat::SingleBuffer);
m_offscreenSurface->setFormat(format);
m_offscreenSurface->create();
- QOpenGLContext *shareContext = 0;
- if (m_surface)
- shareContext = qobject_cast<QOpenGLContext*>(m_surface->property("GLContext").value<QObject*>());
m_glContext = new QOpenGLContext;
m_glContext->setFormat(m_offscreenSurface->requestedFormat());
if (shareContext)
m_glContext->setShareContext(shareContext);
- if (!m_glContext->create())
- return 0;
+ if (!m_glContext->create()) {
+ delete m_glContext;
+ m_glContext = 0;
+ delete m_offscreenSurface;
+ m_offscreenSurface = 0;
+ return false;
+ }
// if sharing contexts is not supported, fallback to image rendering and send the bits
// to the video surface
@@ -228,7 +255,21 @@ jobject QAndroidVideoRendererControl::surfaceHolder()
if (m_surfaceTexture->isValid()) {
connect(m_surfaceTexture, SIGNAL(frameAvailable()), this, SLOT(onFrameAvailable()));
+ } else {
+ delete m_surfaceTexture;
+ m_surfaceTexture = 0;
+ glDeleteTextures(1, &m_externalTex);
+ }
+ return m_surfaceTexture != 0;
+}
+
+jobject QAndroidVideoRendererControl::surfaceHolder()
+{
+ if (!initSurfaceTexture())
+ return 0;
+
+ if (!m_surfaceHolder) {
QJNILocalRef<jobject> surfaceTex = m_surfaceTexture->surfaceTexture();
m_androidSurface = new QJNIObject("android/view/Surface",
@@ -236,16 +277,9 @@ jobject QAndroidVideoRendererControl::surfaceHolder()
surfaceTex.object());
m_surfaceHolder = new JSurfaceTextureHolder(m_androidSurface->object());
- } else {
- delete m_surfaceTexture;
- m_surfaceTexture = 0;
- glDeleteTextures(1, &m_externalTex);
}
- if (m_surfaceHolder)
- return m_surfaceHolder->object();
-
- return 0;
+ return m_surfaceHolder->object();
}
void QAndroidVideoRendererControl::setVideoSize(const QSize &size)
@@ -373,4 +407,18 @@ void QAndroidVideoRendererControl::createGLResources()
}
}
+bool QAndroidVideoRendererControl::eventFilter(QObject *, QEvent *e)
+{
+ if (e->type() == QEvent::DynamicPropertyChange) {
+ QDynamicPropertyChangeEvent *event = static_cast<QDynamicPropertyChangeEvent*>(e);
+ if (event->propertyName() == "GLContext" && m_textureReadyCallback) {
+ m_textureReadyCallback(m_textureReadyContext);
+ m_textureReadyCallback = 0;
+ m_textureReadyContext = 0;
+ }
+ }
+
+ return false;
+}
+
QT_END_NAMESPACE
diff --git a/src/plugins/android/mediaplayer/qandroidvideorendercontrol.h b/src/plugins/android/mediaplayer/qandroidvideorendercontrol.h
index 525291e1f..cd935502c 100644
--- a/src/plugins/android/mediaplayer/qandroidvideorendercontrol.h
+++ b/src/plugins/android/mediaplayer/qandroidvideorendercontrol.h
@@ -65,14 +65,18 @@ public:
void setSurface(QAbstractVideoSurface *surface) Q_DECL_OVERRIDE;
jobject surfaceHolder() Q_DECL_OVERRIDE;
+ bool isTextureReady() Q_DECL_OVERRIDE;
+ void setTextureReadyCallback(TextureReadyCallback cb, void *context = 0) Q_DECL_OVERRIDE;
void setVideoSize(const QSize &size) Q_DECL_OVERRIDE;
void stop() Q_DECL_OVERRIDE;
+ bool eventFilter(QObject *obj, QEvent *event) Q_DECL_OVERRIDE;
+
private Q_SLOTS:
void onFrameAvailable();
private:
- void setupSurface();
+ bool initSurfaceTexture();
void renderFrameToFbo();
void createGLResources();
@@ -88,6 +92,9 @@ private:
JSurfaceTexture *m_surfaceTexture;
JSurfaceTextureHolder *m_surfaceHolder;
uint m_externalTex;
+
+ TextureReadyCallback m_textureReadyCallback;
+ void *m_textureReadyContext;
};
QT_END_NAMESPACE