From: Yoann Lopes Date: Fri, 5 Jul 2013 14:26:55 +0000 (+0200) Subject: Android: wait to have a valid video surface before loading a media. X-Git-Tag: upstream/5.2.95+rc1~69^2~2 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=5e7e8e04d1be82e23f69e2966d355b4aa8d93442;p=platform%2Fupstream%2Fqtmultimedia.git 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 --- diff --git a/src/plugins/android/mediaplayer/qandroidmediaplayercontrol.cpp b/src/plugins/android/mediaplayer/qandroidmediaplayercontrol.cpp index cb34fba..a70f4e1 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(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 445e8de..93eced8 100644 --- a/src/plugins/android/mediaplayer/qandroidmediaplayercontrol.h +++ b/src/plugins/android/mediaplayer/qandroidmediaplayercontrol.h @@ -44,6 +44,7 @@ #include #include +#include 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 99db7c3..d59971f 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 c63e0e7..fe26b45 100644 --- a/src/plugins/android/mediaplayer/qandroidvideorendercontrol.cpp +++ b/src/plugins/android/mediaplayer/qandroidvideorendercontrol.cpp @@ -50,6 +50,7 @@ #include #include #include +#include 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(m_surface->property("GLContext").value()); + 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(m_surface->property("GLContext").value()); 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 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(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 525291e..cd93550 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