blob: ee145d24ec729b199b176802d3591309078e828b [file] [log] [blame]
/*
Copyright (C) 2016 Igalia S.L.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "config.h"
#include "VideoTextureCopierGStreamer.h"
#if USE(GSTREAMER_GL)
#include "FloatRect.h"
#include "GLContext.h"
#include "ImageOrientation.h"
#include "TextureMapperShaderProgram.h"
namespace WebCore {
VideoTextureCopierGStreamer::VideoTextureCopierGStreamer(ColorConversion colorConversion)
{
GLContext* previousContext = GLContext::current();
ASSERT(previousContext);
PlatformDisplay::sharedDisplayForCompositing().sharingGLContext()->makeContextCurrent();
glGenFramebuffers(1, &m_framebuffer);
glGenTextures(1, &m_resultTexture);
#if !USE(OPENGL_ES)
// For OpenGL > 3.2 we need to have a VAO.
if (GLContext::current()->version() >= 320)
glGenVertexArrays(1, &m_vao);
#endif
static const GLfloat vertices[] = { 0, 0, 1, 0, 1, 1, 0, 1 };
glGenBuffers(1, &m_vbo);
glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 8, vertices, GL_STATIC_DRAW);
updateColorConversionMatrix(colorConversion);
updateTextureSpaceMatrix();
previousContext->makeContextCurrent();
}
VideoTextureCopierGStreamer::~VideoTextureCopierGStreamer()
{
GLContext* previousContext = GLContext::current();
PlatformDisplay::sharedDisplayForCompositing().sharingGLContext()->makeContextCurrent();
glDeleteFramebuffers(1, &m_framebuffer);
glDeleteBuffers(1, &m_vbo);
glDeleteTextures(1, &m_resultTexture);
#if !USE(OPENGL_ES)
if (m_vao)
glDeleteVertexArrays(1, &m_vao);
#endif
m_shaderProgram = nullptr;
if (previousContext)
previousContext->makeContextCurrent();
}
void VideoTextureCopierGStreamer::updateColorConversionMatrix(ColorConversion colorConversion)
{
switch (colorConversion) {
case ColorConversion::ConvertBGRAToRGBA:
m_colorConversionMatrix.setMatrix(0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0);
break;
case ColorConversion::ConvertARGBToRGBA:
m_colorConversionMatrix.setMatrix(0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0);
break;
case ColorConversion::NoConvert:
m_colorConversionMatrix.makeIdentity();
break;
default:
RELEASE_ASSERT_NOT_REACHED();
}
}
void VideoTextureCopierGStreamer::updateTextureSpaceMatrix()
{
m_textureSpaceMatrix.makeIdentity();
switch (m_orientation) {
case ImageOrientation::OriginTopLeft:
break;
case ImageOrientation::OriginRightTop:
m_textureSpaceMatrix.rotate(-90);
m_textureSpaceMatrix.translate(-1, 0);
break;
case ImageOrientation::OriginBottomRight:
m_textureSpaceMatrix.rotate(180);
m_textureSpaceMatrix.translate(-1, -1);
break;
case ImageOrientation::OriginLeftBottom:
m_textureSpaceMatrix.rotate(-270);
m_textureSpaceMatrix.translate(0, -1);
break;
default:
ASSERT_NOT_REACHED();
}
if (!m_flipY) {
m_textureSpaceMatrix.flipY();
m_textureSpaceMatrix.translate(0, -1);
}
}
void VideoTextureCopierGStreamer::updateTransformationMatrix()
{
FloatRect targetRect = FloatRect(FloatPoint(), m_size);
TransformationMatrix identityMatrix;
m_modelViewMatrix = TransformationMatrix(identityMatrix).multiply(TransformationMatrix::rectToRect(FloatRect(0, 0, 1, 1), targetRect));
// Taken from TextureMapperGL.
const float nearValue = 9999999;
const float farValue = -99999;
m_projectionMatrix = TransformationMatrix(2.0 / float(m_size.width()), 0, 0, 0,
0, (-2.0) / float(m_size.height()), 0, 0,
0, 0, -2.f / (farValue - nearValue), 0,
-1, 1, -(farValue + nearValue) / (farValue - nearValue), 1);
}
bool VideoTextureCopierGStreamer::copyVideoTextureToPlatformTexture(TextureMapperPlatformLayerBuffer& inputTexture, IntSize& frameSize, GLuint outputTexture, GLenum outputTarget, GLint level, GLenum internalFormat, GLenum format, GLenum type, bool flipY, ImageOrientation sourceOrientation)
{
if (!m_framebuffer || !m_vbo || frameSize.isEmpty())
return false;
if (m_size != frameSize) {
m_size = frameSize;
updateTransformationMatrix();
}
if (m_flipY != flipY || m_orientation != sourceOrientation) {
m_flipY = flipY;
m_orientation = sourceOrientation;
updateTextureSpaceMatrix();
}
// Save previous context and activate the sharing one.
GLContext* previousContext = GLContext::current();
ASSERT(previousContext);
PlatformDisplay::sharedDisplayForCompositing().sharingGLContext()->makeContextCurrent();
// Determine what shader program to use and create it if necessary.
using Buffer = TextureMapperPlatformLayerBuffer;
TextureMapperShaderProgram::Options options;
WTF::switchOn(inputTexture.textureVariant(),
[&](const Buffer::RGBTexture&) { options = TextureMapperShaderProgram::TextureRGB; },
[&](const Buffer::YUVTexture& texture) {
switch (texture.numberOfPlanes) {
case 1:
ASSERT(texture.yuvPlane[0] == texture.yuvPlane[1] && texture.yuvPlane[1] == texture.yuvPlane[2]);
ASSERT(texture.yuvPlaneOffset[0] == 2 && texture.yuvPlaneOffset[1] == 1 && !texture.yuvPlaneOffset[2]);
options = TextureMapperShaderProgram::TexturePackedYUV;
break;
case 2:
ASSERT(!texture.yuvPlaneOffset[0]);
options = texture.yuvPlaneOffset[1] ? TextureMapperShaderProgram::TextureNV21 : TextureMapperShaderProgram::TextureNV12;
break;
case 3:
ASSERT(!texture.yuvPlaneOffset[0] && !texture.yuvPlaneOffset[1] && !texture.yuvPlaneOffset[2]);
options = TextureMapperShaderProgram::TextureYUV;
break;
}
});
if (options != m_shaderOptions) {
m_shaderProgram = TextureMapperShaderProgram::create(options);
m_shaderOptions = options;
}
if (!m_shaderProgram) {
previousContext->makeContextCurrent();
return false;
}
// Save previous bound framebuffer, texture and viewport.
GLint boundFramebuffer = 0;
GLint boundTexture = 0;
GLint previousViewport[4] = { 0, 0, 0, 0};
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &boundFramebuffer);
glGetIntegerv(GL_TEXTURE_BINDING_2D, &boundTexture);
glGetIntegerv(GL_VIEWPORT, previousViewport);
// Use our own output texture if we are not given one.
if (!outputTexture)
outputTexture = m_resultTexture;
// Set proper parameters to the output texture and allocate uninitialized memory for it.
glBindTexture(outputTarget, outputTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(outputTarget, level, internalFormat, m_size.width(), m_size.height(), 0, format, type, nullptr);
// Bind framebuffer to paint and attach the destination texture to it.
glBindFramebuffer(GL_FRAMEBUFFER, m_framebuffer);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, outputTexture, 0);
// Set the viewport.
glViewport(0, 0, m_size.width(), m_size.height());
// Set program parameters.
glUseProgram(m_shaderProgram->programID());
WTF::switchOn(inputTexture.textureVariant(),
[&](const Buffer::RGBTexture& texture) {
glUniform1i(m_shaderProgram->samplerLocation(), 0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture.id);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
},
[&](const Buffer::YUVTexture& texture) {
switch (texture.numberOfPlanes) {
case 1:
glUniform1i(m_shaderProgram->samplerLocation(), texture.yuvPlane[0]);
break;
case 2:
glUniform1i(m_shaderProgram->samplerYLocation(), texture.yuvPlane[0]);
glUniform1i(m_shaderProgram->samplerULocation(), texture.yuvPlane[1]);
break;
case 3:
glUniform1i(m_shaderProgram->samplerYLocation(), texture.yuvPlane[0]);
glUniform1i(m_shaderProgram->samplerULocation(), texture.yuvPlane[1]);
glUniform1i(m_shaderProgram->samplerVLocation(), texture.yuvPlane[2]);
break;
}
glUniformMatrix3fv(m_shaderProgram->yuvToRgbLocation(), 1, GL_FALSE, static_cast<const GLfloat *>(&texture.yuvToRgbMatrix[0]));
for (int i = texture.numberOfPlanes - 1; i >= 0; --i) {
glActiveTexture(GL_TEXTURE0 + i);
glBindTexture(GL_TEXTURE_2D, texture.planes[i]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}
});
m_shaderProgram->setMatrix(m_shaderProgram->modelViewMatrixLocation(), m_modelViewMatrix);
m_shaderProgram->setMatrix(m_shaderProgram->projectionMatrixLocation(), m_projectionMatrix);
m_shaderProgram->setMatrix(m_shaderProgram->textureSpaceMatrixLocation(), m_textureSpaceMatrix);
m_shaderProgram->setMatrix(m_shaderProgram->textureColorSpaceMatrixLocation(), m_colorConversionMatrix);
// Perform the copy.
#if !USE(OPENGL_ES)
if (GLContext::current()->version() >= 320 && m_vao)
glBindVertexArray(m_vao);
#endif
glEnableVertexAttribArray(m_shaderProgram->vertexLocation());
glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
glVertexAttribPointer(m_shaderProgram->vertexLocation(), 2, GL_FLOAT, false, 0, 0);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glDisableVertexAttribArray(m_shaderProgram->vertexLocation());
glUseProgram(0);
// Restore previous bindings and viewport.
glBindFramebuffer(GL_FRAMEBUFFER, boundFramebuffer);
glBindTexture(outputTarget, boundTexture);
glViewport(previousViewport[0], previousViewport[1], previousViewport[2], previousViewport[3]);
bool ok = (glGetError() == GL_NO_ERROR);
// Restore previous context.
previousContext->makeContextCurrent();
return ok;
}
} // namespace WebCore
#endif // USE(GSTREAMER_GL)