Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(669)

Unified Diff: ui/cc/cc/CCVideoLayerImpl.cpp

Issue 10701016: Initial import attempt, just to play with. Many things disabled/removed (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « ui/cc/cc/CCVideoLayerImpl.h ('k') | ui/cc/cc/CCYUVVideoDrawQuad.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: ui/cc/cc/CCVideoLayerImpl.cpp
diff --git a/ui/cc/cc/CCVideoLayerImpl.cpp b/ui/cc/cc/CCVideoLayerImpl.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..99a5f4f8bdaf9809dfaf718b1a803583df1cfdf0
--- /dev/null
+++ b/ui/cc/cc/CCVideoLayerImpl.cpp
@@ -0,0 +1,420 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "cc/CCVideoLayerImpl.h"
+
+#include "Extensions3DChromium.h"
+#include "GraphicsContext3D.h"
+#include "LayerRendererChromium.h" // For GLC macro
+#include "LayerTextureSubImage.h"
+#include "NotImplemented.h"
+#include "TextStream.h"
+#include "TextureManager.h" // For TextureAllocator
+#include "cc/CCGraphicsContext.h"
+#include "cc/CCLayerTreeHostImpl.h"
+#include "cc/CCProxy.h"
+#include "cc/CCQuadCuller.h"
+#include "cc/CCStreamVideoDrawQuad.h"
+#include "cc/CCTextureDrawQuad.h"
+#include "cc/CCYUVVideoDrawQuad.h"
+#include <public/WebVideoFrame.h>
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+CCVideoLayerImpl::CCVideoLayerImpl(int id, WebKit::WebVideoFrameProvider* provider)
+ : CCLayerImpl(id)
+ , m_provider(provider)
+ , m_frame(0)
+{
+ // This matrix is the default transformation for stream textures, and flips on the Y axis.
+ m_streamTextureMatrix = WebKit::WebTransformationMatrix(
+ 1, 0, 0, 0,
+ 0, -1, 0, 0,
+ 0, 0, 1, 0,
+ 0, 1, 0, 1);
+
+ // This only happens during a commit on the compositor thread while the main
+ // thread is blocked. That makes this a thread-safe call to set the video
+ // frame provider client that does not require a lock. The same is true of
+ // the call in the destructor.
+ ASSERT(CCProxy::isMainThreadBlocked());
+ m_provider->setVideoFrameProviderClient(this);
+}
+
+CCVideoLayerImpl::~CCVideoLayerImpl()
+{
+ // See comment in constructor for why this doesn't need a lock.
+ ASSERT(CCProxy::isMainThreadBlocked());
+ if (m_provider) {
+ m_provider->setVideoFrameProviderClient(0);
+ m_provider = 0;
+ }
+ freePlaneData(layerTreeHostImpl()->context());
+
+#if !ASSERT_DISABLED
+ for (unsigned i = 0; i < WebKit::WebVideoFrame::maxPlanes; ++i)
+ ASSERT(!m_framePlanes[i].textureId);
+#endif
+}
+
+void CCVideoLayerImpl::stopUsingProvider()
+{
+ // Block the provider from shutting down until this client is done
+ // using the frame.
+ MutexLocker locker(m_providerMutex);
+ ASSERT(!m_frame);
+ m_provider = 0;
+}
+
+// Convert WebKit::WebVideoFrame::Format to GraphicsContext3D's format enum values.
+static GC3Denum convertVFCFormatToGC3DFormat(const WebKit::WebVideoFrame& frame)
+{
+ switch (frame.format()) {
+ case WebKit::WebVideoFrame::FormatYV12:
+ case WebKit::WebVideoFrame::FormatYV16:
+ return GL_LUMINANCE;
+ case WebKit::WebVideoFrame::FormatNativeTexture:
+ return frame.textureTarget();
+ case WebKit::WebVideoFrame::FormatInvalid:
+ case WebKit::WebVideoFrame::FormatRGB32:
+ case WebKit::WebVideoFrame::FormatEmpty:
+ case WebKit::WebVideoFrame::FormatI420:
+ notImplemented();
+ }
+ return GL_INVALID_VALUE;
+}
+
+void CCVideoLayerImpl::willDraw(CCRenderer* layerRenderer, CCGraphicsContext* context)
+{
+ ASSERT(CCProxy::isImplThread());
+ CCLayerImpl::willDraw(layerRenderer, context);
+
+ // Explicitly lock and unlock the provider mutex so it can be held from
+ // willDraw to didDraw. Since the compositor thread is in the middle of
+ // drawing, the layer will not be destroyed before didDraw is called.
+ // Therefore, the only thing that will prevent this lock from being released
+ // is the GPU process locking it. As the GPU process can't cause the
+ // destruction of the provider (calling stopUsingProvider), holding this
+ // lock should not cause a deadlock.
+ m_providerMutex.lock();
+
+ willDrawInternal(layerRenderer, context);
+ freeUnusedPlaneData(context);
+
+ if (!m_frame)
+ m_providerMutex.unlock();
+}
+
+void CCVideoLayerImpl::willDrawInternal(CCRenderer* layerRenderer, CCGraphicsContext* context)
+{
+ ASSERT(CCProxy::isImplThread());
+
+ if (!m_provider) {
+ m_frame = 0;
+ return;
+ }
+
+ m_frame = m_provider->getCurrentFrame();
+
+ if (!m_frame)
+ return;
+
+ m_format = convertVFCFormatToGC3DFormat(*m_frame);
+
+ if (m_format == GL_INVALID_VALUE) {
+ m_provider->putCurrentFrame(m_frame);
+ m_frame = 0;
+ return;
+ }
+
+ if (m_frame->planes() > WebKit::WebVideoFrame::maxPlanes) {
+ m_provider->putCurrentFrame(m_frame);
+ m_frame = 0;
+ return;
+ }
+
+ if (!allocatePlaneData(layerRenderer, context)) {
+ m_provider->putCurrentFrame(m_frame);
+ m_frame = 0;
+ return;
+ }
+
+ if (!copyPlaneData(layerRenderer, context)) {
+ m_provider->putCurrentFrame(m_frame);
+ m_frame = 0;
+ return;
+ }
+}
+
+void CCVideoLayerImpl::appendQuads(CCQuadCuller& quadList, const CCSharedQuadState* sharedQuadState, bool&)
+{
+ ASSERT(CCProxy::isImplThread());
+
+ if (!m_frame)
+ return;
+
+ // FIXME: When we pass quads out of process, we need to double-buffer, or
+ // otherwise synchonize use of all textures in the quad.
+
+ IntRect quadRect(IntPoint(), bounds());
+
+ switch (m_format) {
+ case GL_LUMINANCE: {
+ // YUV software decoder.
+ const FramePlane& yPlane = m_framePlanes[WebKit::WebVideoFrame::yPlane];
+ const FramePlane& uPlane = m_framePlanes[WebKit::WebVideoFrame::uPlane];
+ const FramePlane& vPlane = m_framePlanes[WebKit::WebVideoFrame::vPlane];
+ OwnPtr<CCYUVVideoDrawQuad> yuvVideoQuad = CCYUVVideoDrawQuad::create(sharedQuadState, quadRect, yPlane, uPlane, vPlane);
+ quadList.append(yuvVideoQuad.release());
+ break;
+ }
+ case GL_RGBA: {
+ // RGBA software decoder.
+ const FramePlane& plane = m_framePlanes[WebKit::WebVideoFrame::rgbPlane];
+ float widthScaleFactor = static_cast<float>(plane.visibleSize.width()) / plane.size.width();
+
+ bool premultipliedAlpha = true;
+ FloatRect uvRect(0, 0, widthScaleFactor, 1);
+ bool flipped = false;
+ OwnPtr<CCTextureDrawQuad> textureQuad = CCTextureDrawQuad::create(sharedQuadState, quadRect, plane.textureId, premultipliedAlpha, uvRect, flipped);
+ quadList.append(textureQuad.release());
+ break;
+ }
+ case GL_TEXTURE_2D: {
+ // NativeTexture hardware decoder.
+ bool premultipliedAlpha = true;
+ FloatRect uvRect(0, 0, 1, 1);
+#if defined(OS_CHROMEOS) && defined(__ARMEL__)
+ bool flipped = true; // Under the covers, implemented by OpenMAX IL.
+#elif OS(WINDOWS)
+ bool flipped = true; // Under the covers, implemented by DXVA.
+#else
+ bool flipped = false; // LibVA (cros/intel), MacOS.
+#endif
+ OwnPtr<CCTextureDrawQuad> textureQuad = CCTextureDrawQuad::create(sharedQuadState, quadRect, m_frame->textureId(), premultipliedAlpha, uvRect, flipped);
+ quadList.append(textureQuad.release());
+ break;
+ }
+ case GL_TEXTURE_EXTERNAL_OES: {
+ // StreamTexture hardware decoder.
+ OwnPtr<CCStreamVideoDrawQuad> streamVideoQuad = CCStreamVideoDrawQuad::create(sharedQuadState, quadRect, m_frame->textureId(), m_streamTextureMatrix);
+ quadList.append(streamVideoQuad.release());
+ break;
+ }
+ default:
+ CRASH(); // Someone updated convertVFCFormatToGC3DFormat above but update this!
+ }
+}
+
+void CCVideoLayerImpl::didDraw()
+{
+ ASSERT(CCProxy::isImplThread());
+ CCLayerImpl::didDraw();
+
+ if (!m_frame)
+ return;
+
+ m_provider->putCurrentFrame(m_frame);
+ m_frame = 0;
+
+ m_providerMutex.unlock();
+}
+
+static int videoFrameDimension(int originalDimension, unsigned plane, int format)
+{
+ if (format == WebKit::WebVideoFrame::FormatYV12 && plane != WebKit::WebVideoFrame::yPlane)
+ return originalDimension / 2;
+ return originalDimension;
+}
+
+static bool hasPaddingBytes(const WebKit::WebVideoFrame& frame, unsigned plane)
+{
+ return frame.stride(plane) > videoFrameDimension(frame.width(), plane, frame.format());
+}
+
+IntSize CCVideoLayerImpl::computeVisibleSize(const WebKit::WebVideoFrame& frame, unsigned plane)
+{
+ int visibleWidth = videoFrameDimension(frame.width(), plane, frame.format());
+ int originalWidth = visibleWidth;
+ int visibleHeight = videoFrameDimension(frame.height(), plane, frame.format());
+
+ // When there are dead pixels at the edge of the texture, decrease
+ // the frame width by 1 to prevent the rightmost pixels from
+ // interpolating with the dead pixels.
+ if (hasPaddingBytes(frame, plane))
+ --visibleWidth;
+
+ // In YV12, every 2x2 square of Y values corresponds to one U and
+ // one V value. If we decrease the width of the UV plane, we must decrease the
+ // width of the Y texture by 2 for proper alignment. This must happen
+ // always, even if Y's texture does not have padding bytes.
+ if (plane == WebKit::WebVideoFrame::yPlane && frame.format() == WebKit::WebVideoFrame::FormatYV12) {
+ if (hasPaddingBytes(frame, WebKit::WebVideoFrame::uPlane))
+ visibleWidth = originalWidth - 2;
+ }
+
+ return IntSize(visibleWidth, visibleHeight);
+}
+
+bool CCVideoLayerImpl::FramePlane::allocateData(CCGraphicsContext* context)
+{
+ if (textureId)
+ return true;
+
+ WebKit::WebGraphicsContext3D* context3D = context->context3D();
+ if (!context3D)
+ return false;
+
+ GLC(context3D, textureId = context3D->createTexture());
+ GLC(context3D, context3D->bindTexture(GL_TEXTURE_2D, textureId));
+ // Do basic linear filtering on resize.
+ GLC(context3D, context3D->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
+ GLC(context3D, context3D->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
+ // NPOT textures in GL ES only work when the wrap mode is set to GL_CLAMP_TO_EDGE.
+ GLC(context3D, context3D->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
+ GLC(context3D, context3D->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
+
+ GLC(context3D, context3D->texImage2D(GL_TEXTURE_2D, 0, format, size.width(), size.height(), 0, format, GL_UNSIGNED_BYTE, 0));
+
+ return textureId;
+}
+
+void CCVideoLayerImpl::FramePlane::freeData(CCGraphicsContext* context)
+{
+ if (!textureId)
+ return;
+
+ WebKit::WebGraphicsContext3D* context3D = context->context3D();
+ if (!context3D)
+ return;
+
+ GLC(context3D, context3D->deleteTexture(textureId));
+ textureId = 0;
+}
+
+bool CCVideoLayerImpl::allocatePlaneData(CCRenderer* layerRenderer, CCGraphicsContext* context)
+{
+ int maxTextureSize = layerRenderer->capabilities().maxTextureSize;
+ for (unsigned planeIndex = 0; planeIndex < m_frame->planes(); ++planeIndex) {
+ CCVideoLayerImpl::FramePlane& plane = m_framePlanes[planeIndex];
+
+ IntSize requiredTextureSize(m_frame->stride(planeIndex), videoFrameDimension(m_frame->height(), planeIndex, m_frame->format()));
+ // FIXME: Remove the test against maxTextureSize when tiled layers are implemented.
+ if (requiredTextureSize.isZero() || requiredTextureSize.width() > maxTextureSize || requiredTextureSize.height() > maxTextureSize)
+ return false;
+
+ if (plane.size != requiredTextureSize || plane.format != m_format) {
+ plane.freeData(context);
+ plane.size = requiredTextureSize;
+ plane.format = m_format;
+ }
+
+ if (!plane.textureId) {
+ if (!plane.allocateData(context))
+ return false;
+ plane.visibleSize = computeVisibleSize(*m_frame, planeIndex);
+ }
+ }
+ return true;
+}
+
+bool CCVideoLayerImpl::copyPlaneData(CCRenderer* layerRenderer, CCGraphicsContext* context)
+{
+ size_t softwarePlaneCount = m_frame->planes();
+ if (!softwarePlaneCount)
+ return true;
+
+ WebKit::WebGraphicsContext3D* context3d = context->context3D();
+ if (!context3d) {
+ // FIXME: Implement this path for software compositing.
+ return false;
+ }
+
+ LayerTextureSubImage uploader(true);
+ for (size_t softwarePlaneIndex = 0; softwarePlaneIndex < softwarePlaneCount; ++softwarePlaneIndex) {
+ CCVideoLayerImpl::FramePlane& plane = m_framePlanes[softwarePlaneIndex];
+ const uint8_t* softwarePlanePixels = static_cast<const uint8_t*>(m_frame->data(softwarePlaneIndex));
+ IntRect planeRect(IntPoint(), plane.size);
+
+ context3d->bindTexture(GL_TEXTURE_2D, plane.textureId);
+ uploader.setSubImageSize(plane.size);
+ uploader.upload(softwarePlanePixels, planeRect, planeRect, planeRect, plane.format, context);
+ }
+ return true;
+}
+
+void CCVideoLayerImpl::freePlaneData(CCGraphicsContext* context)
+{
+ for (unsigned i = 0; i < WebKit::WebVideoFrame::maxPlanes; ++i)
+ m_framePlanes[i].freeData(context);
+}
+
+void CCVideoLayerImpl::freeUnusedPlaneData(CCGraphicsContext* context)
+{
+ unsigned firstUnusedPlane = m_frame ? m_frame->planes() : 0;
+ for (unsigned i = firstUnusedPlane; i < WebKit::WebVideoFrame::maxPlanes; ++i)
+ m_framePlanes[i].freeData(context);
+}
+
+void CCVideoLayerImpl::didReceiveFrame()
+{
+ setNeedsRedraw();
+}
+
+void CCVideoLayerImpl::didUpdateMatrix(const float matrix[16])
+{
+ m_streamTextureMatrix = WebKit::WebTransformationMatrix(
+ matrix[0], matrix[1], matrix[2], matrix[3],
+ matrix[4], matrix[5], matrix[6], matrix[7],
+ matrix[8], matrix[9], matrix[10], matrix[11],
+ matrix[12], matrix[13], matrix[14], matrix[15]);
+ setNeedsRedraw();
+}
+
+void CCVideoLayerImpl::didLoseContext()
+{
+ freePlaneData(layerTreeHostImpl()->context());
+}
+
+void CCVideoLayerImpl::setNeedsRedraw()
+{
+ layerTreeHostImpl()->setNeedsRedraw();
+}
+
+void CCVideoLayerImpl::dumpLayerProperties(TextStream& ts, int indent) const
+{
+ writeIndent(ts, indent);
+ ts << "video layer\n";
+ CCLayerImpl::dumpLayerProperties(ts, indent);
+}
+
+}
+
+#endif // USE(ACCELERATED_COMPOSITING)
« no previous file with comments | « ui/cc/cc/CCVideoLayerImpl.h ('k') | ui/cc/cc/CCYUVVideoDrawQuad.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698