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>
This commit is contained in:
committed by
The Qt Project
parent
1dfbe44d90
commit
5e7e8e04d1
@@ -45,6 +45,12 @@
|
|||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
static void textureReadyCallback(void *context)
|
||||||
|
{
|
||||||
|
if (context)
|
||||||
|
reinterpret_cast<QAndroidMediaPlayerControl *>(context)->onSurfaceTextureReady();
|
||||||
|
}
|
||||||
|
|
||||||
QAndroidMediaPlayerControl::QAndroidMediaPlayerControl(QObject *parent)
|
QAndroidMediaPlayerControl::QAndroidMediaPlayerControl(QObject *parent)
|
||||||
: QMediaPlayerControl(parent),
|
: QMediaPlayerControl(parent),
|
||||||
mMediaPlayer(new JMediaPlayer),
|
mMediaPlayer(new JMediaPlayer),
|
||||||
@@ -58,7 +64,8 @@ QAndroidMediaPlayerControl::QAndroidMediaPlayerControl(QObject *parent)
|
|||||||
mVideoAvailable(false),
|
mVideoAvailable(false),
|
||||||
mBuffering(false),
|
mBuffering(false),
|
||||||
mMediaPlayerReady(false),
|
mMediaPlayerReady(false),
|
||||||
mPendingPosition(-1)
|
mPendingPosition(-1),
|
||||||
|
mPendingSetMedia(false)
|
||||||
{
|
{
|
||||||
connect(mMediaPlayer, SIGNAL(bufferingUpdate(qint32)),
|
connect(mMediaPlayer, SIGNAL(bufferingUpdate(qint32)),
|
||||||
this, SLOT(onBufferChanged(qint32)));
|
this, SLOT(onBufferChanged(qint32)));
|
||||||
@@ -208,6 +215,13 @@ void QAndroidMediaPlayerControl::setMedia(const QMediaContent &mediaContent,
|
|||||||
mMediaContent = mediaContent;
|
mMediaContent = mediaContent;
|
||||||
mMediaStream = stream;
|
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();
|
const QString uri = mediaContent.canonicalUrl().toString();
|
||||||
|
|
||||||
if (!uri.isEmpty())
|
if (!uri.isEmpty())
|
||||||
@@ -231,6 +245,13 @@ void QAndroidMediaPlayerControl::setVideoOutput(QAndroidVideoOutput *videoOutput
|
|||||||
mVideoOutput->stop();
|
mVideoOutput->stop();
|
||||||
|
|
||||||
mVideoOutput = videoOutput;
|
mVideoOutput = videoOutput;
|
||||||
|
|
||||||
|
if (mVideoOutput && !mMediaPlayer->display()) {
|
||||||
|
if (mVideoOutput->isTextureReady())
|
||||||
|
mMediaPlayer->setDisplay(mVideoOutput->surfaceHolder());
|
||||||
|
else
|
||||||
|
mVideoOutput->setTextureReadyCallback(textureReadyCallback, this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void QAndroidMediaPlayerControl::play()
|
void QAndroidMediaPlayerControl::play()
|
||||||
@@ -239,7 +260,8 @@ void QAndroidMediaPlayerControl::play()
|
|||||||
mPendingState = QMediaPlayer::PlayingState;
|
mPendingState = QMediaPlayer::PlayingState;
|
||||||
if (mCurrentState == QMediaPlayer::StoppedState
|
if (mCurrentState == QMediaPlayer::StoppedState
|
||||||
&& !mMediaContent.isNull()
|
&& !mMediaContent.isNull()
|
||||||
&& mCurrentMediaStatus != QMediaPlayer::LoadingMedia) {
|
&& mCurrentMediaStatus != QMediaPlayer::LoadingMedia
|
||||||
|
&& !mPendingSetMedia) {
|
||||||
setMedia(mMediaContent, 0);
|
setMedia(mMediaContent, 0);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@@ -392,16 +414,23 @@ void QAndroidMediaPlayerControl::onBufferChanged(qint32 percent)
|
|||||||
|
|
||||||
void QAndroidMediaPlayerControl::onVideoSizeChanged(qint32 width, qint32 height)
|
void QAndroidMediaPlayerControl::onVideoSizeChanged(qint32 width, qint32 height)
|
||||||
{
|
{
|
||||||
if (width == 0 || height == 0)
|
QSize newSize(width, height);
|
||||||
|
|
||||||
|
if (width == 0 || height == 0 || newSize == mVideoSize)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
setVideoAvailable(true);
|
setVideoAvailable(true);
|
||||||
|
mVideoSize = newSize;
|
||||||
|
|
||||||
if (mVideoOutput) {
|
if (mVideoOutput)
|
||||||
if (!mMediaPlayer->display())
|
mVideoOutput->setVideoSize(mVideoSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAndroidMediaPlayerControl::onSurfaceTextureReady()
|
||||||
|
{
|
||||||
|
if (!mMediaPlayer->display() && mVideoOutput) {
|
||||||
mMediaPlayer->setDisplay(mVideoOutput->surfaceHolder());
|
mMediaPlayer->setDisplay(mVideoOutput->surfaceHolder());
|
||||||
if (mMediaPlayer->display())
|
flushPendingStates();
|
||||||
mVideoOutput->setVideoSize(QSize(width, height));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -465,6 +494,9 @@ void QAndroidMediaPlayerControl::setVideoAvailable(bool available)
|
|||||||
if (mVideoAvailable == available)
|
if (mVideoAvailable == available)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (!available)
|
||||||
|
mVideoSize = QSize();
|
||||||
|
|
||||||
mVideoAvailable = available;
|
mVideoAvailable = available;
|
||||||
Q_EMIT videoAvailableChanged(mVideoAvailable);
|
Q_EMIT videoAvailableChanged(mVideoAvailable);
|
||||||
}
|
}
|
||||||
@@ -479,6 +511,12 @@ void QAndroidMediaPlayerControl::resetBufferingProgress()
|
|||||||
|
|
||||||
void QAndroidMediaPlayerControl::flushPendingStates()
|
void QAndroidMediaPlayerControl::flushPendingStates()
|
||||||
{
|
{
|
||||||
|
if (mPendingSetMedia) {
|
||||||
|
setMedia(mMediaContent, 0);
|
||||||
|
mPendingSetMedia = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
switch (mPendingState) {
|
switch (mPendingState) {
|
||||||
case QMediaPlayer::PlayingState:
|
case QMediaPlayer::PlayingState:
|
||||||
if (mPendingPosition > -1)
|
if (mPendingPosition > -1)
|
||||||
|
|||||||
@@ -44,6 +44,7 @@
|
|||||||
|
|
||||||
#include <qglobal.h>
|
#include <qglobal.h>
|
||||||
#include <QMediaPlayerControl>
|
#include <QMediaPlayerControl>
|
||||||
|
#include <qsize.h>
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
@@ -75,6 +76,7 @@ public:
|
|||||||
void setMedia(const QMediaContent &mediaContent, QIODevice *stream) Q_DECL_OVERRIDE;
|
void setMedia(const QMediaContent &mediaContent, QIODevice *stream) Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
void setVideoOutput(QAndroidVideoOutput *videoOutput);
|
void setVideoOutput(QAndroidVideoOutput *videoOutput);
|
||||||
|
void onSurfaceTextureReady();
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void metaDataUpdated();
|
void metaDataUpdated();
|
||||||
@@ -105,11 +107,13 @@ private:
|
|||||||
int mBufferPercent;
|
int mBufferPercent;
|
||||||
bool mAudioAvailable;
|
bool mAudioAvailable;
|
||||||
bool mVideoAvailable;
|
bool mVideoAvailable;
|
||||||
|
QSize mVideoSize;
|
||||||
bool mBuffering;
|
bool mBuffering;
|
||||||
QMediaTimeRange mAvailablePlaybackRange;
|
QMediaTimeRange mAvailablePlaybackRange;
|
||||||
bool mMediaPlayerReady;
|
bool mMediaPlayerReady;
|
||||||
QMediaPlayer::State mPendingState;
|
QMediaPlayer::State mPendingState;
|
||||||
qint64 mPendingPosition;
|
qint64 mPendingPosition;
|
||||||
|
bool mPendingSetMedia;
|
||||||
|
|
||||||
void setState(QMediaPlayer::State state);
|
void setState(QMediaPlayer::State state);
|
||||||
void setMediaStatus(QMediaPlayer::MediaStatus status);
|
void setMediaStatus(QMediaPlayer::MediaStatus status);
|
||||||
|
|||||||
@@ -48,6 +48,8 @@
|
|||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
typedef void (*TextureReadyCallback)(void*);
|
||||||
|
|
||||||
class QAndroidVideoOutput
|
class QAndroidVideoOutput
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -55,6 +57,10 @@ public:
|
|||||||
virtual ~QAndroidVideoOutput() { }
|
virtual ~QAndroidVideoOutput() { }
|
||||||
|
|
||||||
virtual jobject surfaceHolder() = 0;
|
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 setVideoSize(const QSize &size) = 0;
|
||||||
virtual void stop() = 0;
|
virtual void stop() = 0;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -50,6 +50,7 @@
|
|||||||
#include <QVideoSurfaceFormat>
|
#include <QVideoSurfaceFormat>
|
||||||
#include <QOpenGLFunctions>
|
#include <QOpenGLFunctions>
|
||||||
#include <QOpenGLShaderProgram>
|
#include <QOpenGLShaderProgram>
|
||||||
|
#include <qevent.h>
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
@@ -134,6 +135,8 @@ QAndroidVideoRendererControl::QAndroidVideoRendererControl(QObject *parent)
|
|||||||
, m_surfaceTexture(0)
|
, m_surfaceTexture(0)
|
||||||
, m_surfaceHolder(0)
|
, m_surfaceHolder(0)
|
||||||
, m_externalTex(0)
|
, m_externalTex(0)
|
||||||
|
, m_textureReadyCallback(0)
|
||||||
|
, m_textureReadyContext(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,42 +180,66 @@ void QAndroidVideoRendererControl::setSurface(QAbstractVideoSurface *surface)
|
|||||||
if (surface == m_surface)
|
if (surface == m_surface)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (m_surface && m_surface->isActive())
|
if (m_surface && m_surface->isActive()) {
|
||||||
m_surface->stop();
|
m_surface->stop();
|
||||||
|
m_surface->removeEventFilter(this);
|
||||||
|
}
|
||||||
|
|
||||||
m_surface = surface;
|
m_surface = surface;
|
||||||
|
|
||||||
if (m_surface)
|
if (m_surface) {
|
||||||
m_useImage = !m_surface->supportedPixelFormats(QAbstractVideoBuffer::GLTextureHandle).contains(QVideoFrame::Format_BGR32);
|
m_useImage = !m_surface->supportedPixelFormats(QAbstractVideoBuffer::GLTextureHandle).contains(QVideoFrame::Format_BGR32);
|
||||||
|
m_surface->installEventFilter(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
jobject QAndroidVideoRendererControl::surfaceHolder()
|
bool QAndroidVideoRendererControl::isTextureReady()
|
||||||
{
|
{
|
||||||
if (m_surfaceHolder)
|
return QOpenGLContext::currentContext() || (m_surface && m_surface->property("GLContext").isValid());
|
||||||
return m_surfaceHolder->object();
|
}
|
||||||
|
|
||||||
|
void QAndroidVideoRendererControl::setTextureReadyCallback(TextureReadyCallback cb, void *context)
|
||||||
|
{
|
||||||
|
m_textureReadyCallback = cb;
|
||||||
|
m_textureReadyContext = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QAndroidVideoRendererControl::initSurfaceTexture()
|
||||||
|
{
|
||||||
|
if (m_surfaceTexture)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (!m_surface)
|
||||||
|
return false;
|
||||||
|
|
||||||
QOpenGLContext *currContext = QOpenGLContext::currentContext();
|
QOpenGLContext *currContext = QOpenGLContext::currentContext();
|
||||||
|
|
||||||
// If we don't have a GL context in the current thread, create one and share it
|
// If we don't have a GL context in the current thread, create one and share it
|
||||||
// with the render thread GL context
|
// with the render thread GL context
|
||||||
if (!currContext && !m_glContext) {
|
if (!currContext && !m_glContext) {
|
||||||
|
QOpenGLContext *shareContext = qobject_cast<QOpenGLContext*>(m_surface->property("GLContext").value<QObject*>());
|
||||||
|
if (!shareContext)
|
||||||
|
return false;
|
||||||
|
|
||||||
m_offscreenSurface = new QOffscreenSurface;
|
m_offscreenSurface = new QOffscreenSurface;
|
||||||
QSurfaceFormat format;
|
QSurfaceFormat format;
|
||||||
format.setSwapBehavior(QSurfaceFormat::SingleBuffer);
|
format.setSwapBehavior(QSurfaceFormat::SingleBuffer);
|
||||||
m_offscreenSurface->setFormat(format);
|
m_offscreenSurface->setFormat(format);
|
||||||
m_offscreenSurface->create();
|
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 = new QOpenGLContext;
|
||||||
m_glContext->setFormat(m_offscreenSurface->requestedFormat());
|
m_glContext->setFormat(m_offscreenSurface->requestedFormat());
|
||||||
|
|
||||||
if (shareContext)
|
if (shareContext)
|
||||||
m_glContext->setShareContext(shareContext);
|
m_glContext->setShareContext(shareContext);
|
||||||
|
|
||||||
if (!m_glContext->create())
|
if (!m_glContext->create()) {
|
||||||
return 0;
|
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
|
// if sharing contexts is not supported, fallback to image rendering and send the bits
|
||||||
// to the video surface
|
// to the video surface
|
||||||
@@ -228,7 +255,21 @@ jobject QAndroidVideoRendererControl::surfaceHolder()
|
|||||||
|
|
||||||
if (m_surfaceTexture->isValid()) {
|
if (m_surfaceTexture->isValid()) {
|
||||||
connect(m_surfaceTexture, SIGNAL(frameAvailable()), this, SLOT(onFrameAvailable()));
|
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();
|
QJNILocalRef<jobject> surfaceTex = m_surfaceTexture->surfaceTexture();
|
||||||
|
|
||||||
m_androidSurface = new QJNIObject("android/view/Surface",
|
m_androidSurface = new QJNIObject("android/view/Surface",
|
||||||
@@ -236,16 +277,9 @@ jobject QAndroidVideoRendererControl::surfaceHolder()
|
|||||||
surfaceTex.object());
|
surfaceTex.object());
|
||||||
|
|
||||||
m_surfaceHolder = new JSurfaceTextureHolder(m_androidSurface->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 m_surfaceHolder->object();
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void QAndroidVideoRendererControl::setVideoSize(const QSize &size)
|
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
|
QT_END_NAMESPACE
|
||||||
|
|||||||
@@ -65,14 +65,18 @@ public:
|
|||||||
void setSurface(QAbstractVideoSurface *surface) Q_DECL_OVERRIDE;
|
void setSurface(QAbstractVideoSurface *surface) Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
jobject surfaceHolder() 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 setVideoSize(const QSize &size) Q_DECL_OVERRIDE;
|
||||||
void stop() Q_DECL_OVERRIDE;
|
void stop() Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
|
bool eventFilter(QObject *obj, QEvent *event) Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void onFrameAvailable();
|
void onFrameAvailable();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void setupSurface();
|
bool initSurfaceTexture();
|
||||||
void renderFrameToFbo();
|
void renderFrameToFbo();
|
||||||
void createGLResources();
|
void createGLResources();
|
||||||
|
|
||||||
@@ -88,6 +92,9 @@ private:
|
|||||||
JSurfaceTexture *m_surfaceTexture;
|
JSurfaceTexture *m_surfaceTexture;
|
||||||
JSurfaceTextureHolder *m_surfaceHolder;
|
JSurfaceTextureHolder *m_surfaceHolder;
|
||||||
uint m_externalTex;
|
uint m_externalTex;
|
||||||
|
|
||||||
|
TextureReadyCallback m_textureReadyCallback;
|
||||||
|
void *m_textureReadyContext;
|
||||||
};
|
};
|
||||||
|
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
|||||||
Reference in New Issue
Block a user