Permalink
Browse files

webkit: HTML5 video user experience and stability fixes

- Update media player state without requiring base layer
- Show progress spinner over last video frame while buffering
- Fix VideoLayerManager tracking of textures
- Disable javascript rate change if preload not enabled
- Pass video layer ID to HTML5VideoViewProxy during creation.
  This allows the HTML5VideoViewManager in the android framework to
  determine the HTML5VideoViewProxy instance associated with the
  enterFullScreenVideo call before any video is played
- Remove videoLayerId parameter to several JNI functions
- Make animated transition user aspect-ratio-adjusted inline rectangle
- Resume video from saved position after suspend

Change-Id: Idd0853028f8ea1cbfef557263bc86b7bb1a522a5
  • Loading branch information...
1 parent e561f0b commit a235c4e6aa037eb8816ec6edea765d8ccd5d56d2 Denise Cheng committed with Whitehawkx Jul 24, 2012
@@ -186,7 +186,12 @@ HTMLMediaElement::HTMLMediaElement(const QualifiedName& tagName, Document* docum
document->registerForMediaVolumeCallbacks(this);
document->registerForPrivateBrowsingStateChangedCallbacks(this);
#if PLATFORM(ANDROID) && ENABLE(TOUCH_EVENTS)
- m_restrictions |= RequireUserGestureForRateChangeRestriction;
+ // Allow javascript to make rate changes when preload is enabled
+ bool preloadEnabled = document->settings() && document->settings()->mediaPreloadEnabled();
+ if (preloadEnabled)
+ m_userGestureInitiated = true;
+ else
+ m_restrictions |= RequireUserGestureForRateChangeRestriction;
#endif
}
@@ -125,6 +125,7 @@ class MediaPlayerPrivate : public MediaPlayerPrivateInterface {
virtual void prepareEnterFullscreen() { }
virtual void prepareExitFullscreen() { }
VideoLayerObserver* getVideoLayerObserver() { return m_videoLayerObserver; }
+ virtual void updateVideoLayerSize() { }
protected:
// Android-specific methods and fields.
@@ -47,22 +47,18 @@ android::Mutex videoLayerObserverLock;
VideoLayerAndroid::VideoLayerAndroid()
: LayerAndroid((RenderLayer*)0)
+ , m_playerState(INITIALIZED)
+ , m_observer(NULL)
{
- init();
}
VideoLayerAndroid::VideoLayerAndroid(const VideoLayerAndroid& layer)
: LayerAndroid(layer)
-{
- init();
-}
-
-void VideoLayerAndroid::init()
+ , m_observer(NULL)
{
// m_surfaceTexture is only useful on UI thread, no need to copy.
// And it will be set at setBaseLayer timeframe
- m_playerState = INITIALIZED;
- m_observer = NULL;
+ m_playerState = layer.m_playerState;
}
VideoLayerAndroid::~VideoLayerAndroid()
@@ -71,6 +67,10 @@ VideoLayerAndroid::~VideoLayerAndroid()
SkSafeUnref(m_observer);
}
+void VideoLayerAndroid::setPlayerState(PlayerState state) {
+ m_playerState = state;
+}
+
// We can use this function to set the Layer to point to surface texture.
void VideoLayerAndroid::setSurfaceTexture(sp<SurfaceTexture> texture,
int textureName, PlayerState playerState)
@@ -87,16 +87,11 @@ void VideoLayerAndroid::registerVideoLayerObserver(VideoLayerObserverInterface*
SkRefCnt_SafeAssign(m_observer, observer);
}
-void VideoLayerAndroid::showPreparingAnimation(const SkRect& rect,
- const SkRect innerRect)
+void VideoLayerAndroid::showProgressSpinner(const SkRect& innerRect)
{
ShaderProgram* shader = TilesManager::instance()->shader();
VideoLayerManager* manager = TilesManager::instance()->videoLayerManager();
- // Paint the video content's background.
- PureColorQuadData backGroundQuadData(Color(128, 128, 128, 255), LayerQuad,
- &m_drawTransform, &rect);
- shader->drawQuad(&backGroundQuadData);
-
+ // Show the progressing animation, with two rotating circles
TransformationMatrix addReverseRotation;
TransformationMatrix addRotation = m_drawTransform;
addRotation.translate(innerRect.fLeft, innerRect.fTop);
@@ -149,7 +144,7 @@ bool VideoLayerAndroid::drawGL(bool layerTilesDisabled)
ShaderProgram* shader = tilesManager->shader();
SkRect rect = SkRect::MakeSize(getSize());
- GLfloat surfaceMatrix[16];
+ GLfloat surfaceMatrix[surfaceMatrixSize];
// Calculate the video rect based on the aspect ratio and the element rect.
SkRect videoRect = calVideoRect(rect);
@@ -178,11 +173,8 @@ bool VideoLayerAndroid::drawGL(bool layerTilesDisabled)
&m_drawTransform, &innerRect);
// Draw the poster image, the progressing image or the Video depending
// on the player's state.
- if (m_playerState == PREPARING) {
- // Show the progressing animation, with two rotating circles
- showPreparingAnimation(videoRect, innerRect);
- needRedraw = true;
- } else if (m_playerState == PLAYING && m_surfaceTexture.get()) {
+ if ((m_playerState == PREPARED || m_playerState == PLAYING
+ || m_playerState == BUFFERING) && m_surfaceTexture.get()) {
// Show the real video.
m_surfaceTexture->updateTexImage();
m_surfaceTexture->getTransformMatrix(surfaceMatrix);
@@ -191,15 +183,23 @@ bool VideoLayerAndroid::drawGL(bool layerTilesDisabled)
videoRect, textureId);
manager->updateMatrix(uniqueId(), surfaceMatrix);
+ IconType iconType = (m_playerState == PREPARED) ? PauseIcon : PlayIcon;
// Use the scale to control the fading the sizing during animation
- double scale = manager->drawIcon(uniqueId(), PlayIcon);
+ double scale = manager->drawIcon(uniqueId(), iconType);
if (scale) {
innerRect.inset(manager->getButtonSize() / 4 * scale,
manager->getButtonSize() / 4 * scale);
- iconQuadData.updateTextureId(manager->getPlayTextureId());
+ if (m_playerState == PREPARED)
+ iconQuadData.updateTextureId(manager->getPauseTextureId());
+ else
+ iconQuadData.updateTextureId(manager->getPlayTextureId());
iconQuadData.updateOpacity(scale);
shader->drawQuad(&iconQuadData);
needRedraw = true;
+ } else if (m_playerState == BUFFERING) {
+ // Show the spinner on top of the video texture
+ showProgressSpinner(innerRect);
+ needRedraw = true;
}
} else {
GLuint textureId = manager->getTextureId(uniqueId());
@@ -213,26 +213,23 @@ bool VideoLayerAndroid::drawGL(bool layerTilesDisabled)
pureColorQuadData.updateColor(Color(128, 128, 128, 255));
shader->drawQuad(&pureColorQuadData);
- iconQuadData.updateTextureId(manager->getPosterTextureId());
- iconQuadData.updateOpacity(1.0);
- shader->drawQuad(&iconQuadData);
+ // Draw static poster image
+ if (m_playerState != PREPARING) {
+ iconQuadData.updateTextureId(manager->getPosterTextureId());
+ iconQuadData.updateOpacity(1.0);
+ shader->drawQuad(&iconQuadData);
+ }
}
- // Use the scale to control the fading and the sizing during animation.
- double scale = manager->drawIcon(uniqueId(), PauseIcon);
- if (scale) {
- innerRect.inset(manager->getButtonSize() / 4 * scale,
- manager->getButtonSize() / 4 * scale);
- iconQuadData.updateTextureId(manager->getPauseTextureId());
- iconQuadData.updateOpacity(scale);
- shader->drawQuad(&iconQuadData);
+ if (m_playerState == PREPARING) {
+ // Show the progressing animation, with two rotating circles
+ showProgressSpinner(innerRect);
needRedraw = true;
}
-
}
if (m_observer) {
- IntSize size(rect.width(), rect.height());
+ IntSize size(videoRect.width(), videoRect.height());
m_observer->notifyRectChange(TilesManager::instance()->shader()->rectInViewCoord(m_drawTransform, size));
}
@@ -45,7 +45,7 @@ namespace WebCore {
// Otherwise will draw a static image.
// NOTE: These values are matching the ones in HTML5VideoView.java
// Please keep them in sync when changed here.
-typedef enum {INITIALIZED, PREPARING, PREPARED, PLAYING, RELEASED} PlayerState;
+typedef enum {INITIALIZED, PREPARING, PREPARED, PLAYING, BUFFERING, RELEASED} PlayerState;
class VideoLayerObserverInterface : public SkRefCnt {
public:
@@ -68,12 +68,11 @@ class VideoLayerAndroid : public LayerAndroid {
virtual bool drawGL(bool layerTilesDisabled);
void setSurfaceTexture(sp<SurfaceTexture> texture, int textureName, PlayerState playerState);
virtual bool needsIsolatedSurface() { return true; }
+ void setPlayerState(PlayerState state);
void registerVideoLayerObserver(VideoLayerObserverInterface* observer);
-
private:
void init();
- void showPreparingAnimation(const SkRect& rect,
- const SkRect innerRect);
+ void showProgressSpinner(const SkRect& innerRect);
SkRect calVideoRect(const SkRect& rect);
// Surface texture for showing the video is actually allocated in Java side
// and passed into this native code.
@@ -177,7 +177,8 @@ GLfloat* VideoLayerManager::getMatrix(const int layerId)
{
android::Mutex::Autolock lock(m_videoLayerInfoMapLock);
GLfloat* result = 0;
- if (m_videoLayerInfoMap.contains(layerId))
+ VideoLayerInfo* pInfo = m_videoLayerInfoMap.get(layerId);
+ if (pInfo && pInfo->matrixInitialized)
result = m_videoLayerInfoMap.get(layerId)->surfaceMatrix;
return result;
}
@@ -197,26 +198,34 @@ void VideoLayerManager::registerTexture(const int layerId, const GLuint textureI
{
android::Mutex::Autolock lock(m_videoLayerInfoMapLock);
// If the texture has been registered, then early return.
- if (m_videoLayerInfoMap.get(layerId)) {
- GLuint oldTextureId = m_videoLayerInfoMap.get(layerId)->textureId;
- if (oldTextureId != textureId)
- removeLayerInternal(layerId);
- else
- return;
- }
+ VideoLayerInfo* pPreviousInfo = m_videoLayerInfoMap.get(layerId);
+
+ if (pPreviousInfo && pPreviousInfo->textureId == textureId)
+ return;
+
// The old info is deleted and now complete the new info and store it.
VideoLayerInfo* pInfo = new VideoLayerInfo();
pInfo->textureId = textureId;
- memset(pInfo->surfaceMatrix, 0, sizeof(pInfo->surfaceMatrix));
+ if (pPreviousInfo == NULL) {
+ memset(pInfo->surfaceMatrix, 0, sizeof(pInfo->surfaceMatrix));
+ pInfo->matrixInitialized = false;
+ } else {
+ // Need the previous surface matrix to composite the new texture
+ memcpy(pInfo->surfaceMatrix, pPreviousInfo->surfaceMatrix, sizeof(pInfo->surfaceMatrix));
+ pInfo->matrixInitialized = true;
+ // Can now delete the previous entry
+ removeLayerInternal(layerId);
+ }
pInfo->videoSize = 0;
pInfo->aspectRatio = DEFAULT_VIDEO_ASPECT_RATIO;
m_currentTimeStamp++;
pInfo->timeStamp = m_currentTimeStamp;
+ pInfo->canBeRecycled = false;
pInfo->lastIconShownTime = 0;
pInfo->iconState = Registered;
m_videoLayerInfoMap.add(layerId, pInfo);
- ALOGV("GL texture %d regisered for layerId %d", textureId, layerId);
+ ALOGV("GL texture %d registered for layerId %d", textureId, layerId);
return;
}
@@ -256,6 +265,7 @@ void VideoLayerManager::updateMatrix(const int layerId, const GLfloat* matrix)
ASSERT(matrix);
if (pInfo && !memcmp(matrix, pInfo->surfaceMatrix, sizeof(pInfo->surfaceMatrix)))
return;
+ pInfo->matrixInitialized = true;
memcpy(pInfo->surfaceMatrix, matrix, sizeof(pInfo->surfaceMatrix));
} else {
ALOGV("Error: should not reach here, the layerId %d should exist!", layerId);
@@ -264,6 +274,16 @@ void VideoLayerManager::updateMatrix(const int layerId, const GLfloat* matrix)
return;
}
+// When a texture is marked for recycling, the client is indicating that there are no
+// longer any external references to the texture and it may be recycled if needed.
+void VideoLayerManager::markTextureForRecycling(const int layerId, const GLuint textureId)
+{
+ android::Mutex::Autolock lock(m_videoLayerInfoMapLock);
+ if (m_videoLayerInfoMap.contains(layerId)) {
+ m_videoLayerInfoMap.get(layerId)->canBeRecycled = true;
+ }
+}
+
// This is called on the webcore thread, save the GL texture for recycle in
// the retired queue. They will be deleted in deleteUnusedTextures() in the UI
// thread.
@@ -282,7 +302,7 @@ bool VideoLayerManager::recycleTextureMem()
it->first, it->second->textureId, it->second->videoSize, it->second->timeStamp);
#endif
for (InfoIterator it = m_videoLayerInfoMap.begin(); it != end; ++it) {
- if (it->second->timeStamp < oldestTimeStamp) {
+ if (it->second->canBeRecycled && it->second->timeStamp < oldestTimeStamp) {
oldestTimeStamp = it->second->timeStamp;
oldestLayerId = it->first;
}
@@ -47,14 +47,18 @@ enum IconType {
PauseIcon
};
+static const size_t surfaceMatrixSize = 4 * 4;
+
// Every video layer can use its uniqueId to query VideoLayerManager about such
// info globally.
struct VideoLayerInfo {
GLuint textureId; // GL texture bound with the surface texture.
int videoSize; // The size of the video.
float aspectRatio; // The aspect ratio of the video.
int timeStamp; // Used to decide which VideoLayerInfo is the oldest one.
- GLfloat surfaceMatrix[16];
+ GLfloat surfaceMatrix[surfaceMatrixSize];
+ bool matrixInitialized;
+ bool canBeRecycled;
double lastIconShownTime;
IconState iconState;
@@ -76,6 +80,8 @@ class VideoLayerManager {
void updateMatrix(const int layerId, const GLfloat* matrix);
// Remove the layer info from the mapping.
void removeLayer(const int layerId);
+ // Mark this texture to be eligible for recycling if needed
+ void markTextureForRecycling(const int layerId, const GLuint textureId);
// Return the texture name corresponding to the layerId
GLuint getTextureId(const int layerId);
Oops, something went wrong.

0 comments on commit a235c4e

Please sign in to comment.