* Copyright (c) 2025 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "include/render_thread.h"
#include <GLES2/gl2ext.h>
#include <sys/poll.h>
#include <unistd.h>
namespace NativeXComponentSample {
constexpr char DEMO_NAME[] = "HMOSLiveStream";
constexpr uint32_t LOG_PRINT_DOMAIN = 0xFF00;
const std::string TEXTURE_2D_SRC = "#define TEXTURE_2D_SRC 1\n";
const std::string VERSION_GLSL = "#version 300 es\n";
const std::string VERSION310_GLSL = "#version 310 es\n";
constexpr GLuint RECTANGLE_INDICES[] = {
0, 1, 2,
0, 2, 3
};
namespace Detail {
static void CreateGLTex(GLenum target, uint32_t& outTextureId, bool openMipmap = false)
{
uint32_t textureId = 0;
glGenTextures(1, &textureId);
glBindTexture(target, textureId);
glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
if (openMipmap) {
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
} else {
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}
glBindTexture(target, 0);
outTextureId = textureId;
}
static void LeftProd(float* lmatrix, float* rmatrix, float* out)
{
for (int y = 0; y < 4; y++) {
for (int z = 0; z < 4; z++) {
float res = 0;
for (int x = 0; x < 4; x++) {
res += lmatrix[y * 4 + x] * rmatrix[x * 4 + z];
}
out[y * 4 + z] = res;
}
}
}
std::string vertexShader = R"delimiter(
attribute vec3 position;
attribute vec2 texCoord;
varying vec2 vTexCoord;
uniform mat4 matTransform;
void main()
{
gl_Position = vec4(position, 1.0);
vec4 rotatedUV = matTransform * vec4(texCoord, 0.0, 1.0);
vTexCoord = rotatedUV.xy;
}
)delimiter";
std::string fragmentShader = R"delimiter(
#extension GL_EXT_YUV_target : require
#extension GL_OES_EGL_image_external_essl3 : require
precision highp float;
in vec2 vTexCoord;
#if defined(TEXTURE_2D_SRC) && TEXTURE_2D_SRC
uniform sampler2D tex;
#else
//uniform samplerExternalOES tex;
uniform __samplerExternal2DY2YEXT tex;
#endif
layout(yuv) out vec4 outColor;
// out vec4 outColor;
vec3 rgb2yuv_bt2020_limited(vec3 rgb)
{
vec3 RGB2Y = vec3(0.224951, 0.580575, 0.050779);
vec3 RGB2U = vec3(-0.122296, -0.315632, 0.437928);
vec3 RGB2V = vec3(0.437928, -0.402706, -0.035222);
float outY = dot(RGB2Y, rgb) + 0.062561;
float outU = dot(RGB2U, rgb) + 0.500489;
float outV = dot(RGB2V, rgb) + 0.500489;
vec3 YUV;
YUV.x = clamp(outY, 0.0, 1.0);
YUV.y = clamp(outU, 0.0, 1.0);
YUV.z = clamp(outV, 0.0, 1.0);
return YUV;
}
void main()
{
vec4 color = texture(tex, vTexCoord);
#if (defined(TEXTURE_2D_SRC) && TEXTURE_2D_SRC)
color = vec4(rgb2yuv_bt2020_limited(color.rgb), color.a);
#endif
outColor = color;
}
)delimiter";
std::string fragmentShaderBW = R"delimiter(
#extension GL_OES_EGL_image_external : require
precision mediump float;
varying vec2 vTexCoord;
uniform samplerExternalOES texture;
void main()
{
vec4 tc = texture2D(texture, vTexCoord);
float color = tc.r * 0.3 + tc.g * 0.59 + tc.b * 0.11;
gl_FragColor = vec4(color, color, color, 1.0);
}
)delimiter";
std::string frameFragmentShader = R"delimiter(
#extension GL_OES_EGL_image_external : require
precision highp float;
varying vec2 vTexCoord;
uniform samplerExternalOES texture;
void main()
{
gl_FragColor = texture2D(texture, vTexCoord);
}
)delimiter";
GLfloat vertices[] = {
-1.0f, 1.0f, 0.0f, 0.0f, 0.0f,
-1.0f, -1.0f, 0.0f, 0.0f, 1.0f,
1.0f, -1.0f, 0.0f, 1.0f, 1.0f,
1.0f, 1.0f, 0.0f, 1.0f, 0.0f
};
GLuint indices[] = {
0, 1, 2,
0, 2, 3
};
}
RenderThread::RenderThread()
{
Start();
}
RenderThread::~RenderThread() noexcept
{
running_ = false;
if (thread_.joinable()) {
thread_.join();
}
}
bool RenderThread::InitRenderContext()
{
renderContext_ = std::make_unique<EglRenderContext>();
return renderContext_->Init();
}
void RenderThread::DestroyRenderContext()
{
renderContext_.reset();
}
void RenderThread::CleanGLResources()
{
glDeleteVertexArrays(1, &vertexArrayObject_);
glDeleteBuffers(1, &vertexBufferObject_);
glDeleteTextures(1, &inTexId_);
glDeleteTextures(1, &outTexId_);
videoShader_.reset();
imageShader_.reset();
}
bool RenderThread::CreateGLResources()
{
videoShader_ = std::make_unique<NativeXComponentSample::ShaderProgram>(VERSION_GLSL + Detail::vertexShader,
VERSION_GLSL + Detail::fragmentShader);
imageShader_ = std::make_unique<NativeXComponentSample::ShaderProgram>(
VERSION_GLSL + Detail::vertexShader, VERSION_GLSL + TEXTURE_2D_SRC + Detail::fragmentShader);
frameShader_ = std::make_unique<NativeXComponentSample::ShaderProgram>(
Detail::vertexShader, Detail::frameFragmentShader);
if (!videoShader_->Valid() || !imageShader_->Valid()) {
return false;
}
glGenVertexArrays(1, &vertexArrayObject_);
glGenBuffers(1, &vertexBufferObject_);
glBindVertexArray(vertexArrayObject_);
glBindBuffer(GL_ARRAY_BUFFER, vertexBufferObject_);
glBufferData(GL_ARRAY_BUFFER, sizeof(Detail::vertices), Detail::vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void *)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
if (nativeImageTexId_ == 9999) {
Detail::CreateGLTex(GL_TEXTURE_EXTERNAL_OES, nativeImageTexId_);
OH_LOG_Print(LOG_APP, LOG_WARN, LOG_PRINT_DOMAIN, "RenderThread", "nativeImageTexId_:%{public}d",
nativeImageTexId_);
}
if (noiseImageTexId_ == 9999) {
Detail::CreateGLTex(GL_TEXTURE_2D, noiseImageTexId_, true);
OH_LOG_Print(LOG_APP, LOG_WARN, LOG_PRINT_DOMAIN, "RenderThread", "noiseImageTexId_:%{public}d",
noiseImageTexId_);
}
if (outTexId_ == 9999 && inTexId_ == 9999) {
Detail::CreateGLTex(GL_TEXTURE_EXTERNAL_OES, inTexId_);
Detail::CreateGLTex(GL_TEXTURE_EXTERNAL_OES, outTexId_);
glGenFramebuffers(1, &vertexFrameObject_);
}
UpdateImageResource(nullptr, 0, 0, 0);
return true;
}
void RenderThread::UpdateNativeWindow(void *window, uint64_t width, uint64_t height)
{
OH_LOG_Print(LOG_APP, LOG_DEBUG, LOG_PRINT_DOMAIN, "RenderThread", "UpdateNativeWindow,window:%{public}p.", window);
auto nativeWindow = reinterpret_cast<OHNativeWindow *>(window);
xcomponentWindows_ = nativeWindow;
int xcomponentHeight, xcomponentWidth;
OH_NativeWindow_NativeWindowHandleOpt(
xcomponentWindows_, GET_BUFFER_GEOMETRY, &xcomponentHeight, &xcomponentWidth);
OH_LOG_Print(LOG_APP, LOG_DEBUG, LOG_PRINT_DOMAIN, "RenderThread",
"12xcomponentHeight_:%{public}d, xcomponentWidth_:%{public}d", xcomponentHeight, xcomponentWidth);
PostTask([this, nativeWindow, width, height](EglRenderContext &renderContext) {
if (nativeWindow_ != nativeWindow) {
nativeWindow_ = nativeWindow;
if (eglSurface_ != EGL_NO_SURFACE) {
renderContext_->DestroyEglSurface(eglSurface_);
eglSurface_ = EGL_NO_SURFACE;
CleanGLResources();
}
}
if (nativeWindow_ != nullptr) {
int32_t ret = OH_NativeWindow_SetColorSpace(nativeWindow_, OH_COLORSPACE_BT709_LIMIT);
ret |=
OH_NativeWindow_NativeWindowHandleOpt(nativeWindow_, SET_FORMAT, NATIVEBUFFER_PIXEL_FMT_YCBCR_420_SP);
if (ret != 0) {
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "RenderThread",
"nativeWindow_:%{public}p, ret:%{public}d", nativeWindow_, ret);
}
if (eglSurface_ == EGL_NO_SURFACE) {
eglSurface_ = renderContext_->CreateEglSurface(static_cast<EGLNativeWindowType>(nativeWindow_));
}
if (eglSurface_ == EGL_NO_SURFACE) {
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "RenderThread", "xfl CreateEglSurface failed.");
return;
}
renderContext_->MakeCurrent(eglSurface_);
CreateGLResources();
}
});
}
void RenderThread::CreateEncoderSurface(void *window, int32_t width, int32_t height)
{
if (encoderSurface_ != EGL_NO_SURFACE) {
renderContext_->DestroyEglSurface(encoderSurface_);
encoderSurface_ = EGL_NO_SURFACE;
}
if (renderContext_ == nullptr) {
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "RenderThread", "renderContext_ is nullptr");
return;
}
auto nativeWindow = reinterpret_cast<OHNativeWindow *>(window);
PostTask([this, nativeWindow, width, height](EglRenderContext &renderContext) {
if (encoderNativeWindow_ != nativeWindow) {
if (encoderNativeWindow_ != nullptr) {
(void)OH_NativeWindow_NativeObjectUnreference(encoderNativeWindow_);
}
encoderNativeWindow_ = nativeWindow;
if (encoderNativeWindow_ != nullptr) {
(void)OH_NativeWindow_NativeObjectReference(encoderNativeWindow_);
}
if (encoderSurface_ != EGL_NO_SURFACE) {
renderContext_->DestroyEglSurface(encoderSurface_);
encoderSurface_ = EGL_NO_SURFACE;
}
}
if (encoderNativeWindow_ != nullptr) {
(void)OH_NativeWindow_NativeWindowHandleOpt(encoderNativeWindow_, SET_BUFFER_GEOMETRY,
static_cast<int>(width), static_cast<int>(height));
int32_t ret = OH_NativeWindow_SetColorSpace(nativeWindow_, OH_COLORSPACE_BT709_LIMIT);
ret |=
OH_NativeWindow_NativeWindowHandleOpt(nativeWindow_, SET_FORMAT, NATIVEBUFFER_PIXEL_FMT_YCBCR_420_SP);
if (ret != 0) {
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "RenderThread",
"encoderNativeWindow_:%{public}p, ret:%{public}d", nativeWindow_, ret);
}
if (encoderSurface_ == EGL_NO_SURFACE) {
encoderSurface_ =
renderContext_->CreateEglSurface(static_cast<EGLNativeWindowType>(encoderNativeWindow_));
}
if (encoderSurface_ == EGL_NO_SURFACE) {
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "RenderThread", "xfl CreateEglSurface failed.");
return;
}
}
});
encoderWidth = width;
encoderHeight = height;
}
void RenderThread::DeleteEncoderSurface(void)
{
renderContext_->DestroyEglSurface(encoderSurface_);
encoderSurface_ = EGL_NO_SURFACE;
}
void RenderThread::AddBW(void)
{
PostTask([this](EglRenderContext &renderContext) {
videoShader_.reset();
if (isVertexShader) {
isVertexShader = false;
videoShader_ =
std::make_unique<NativeXComponentSample::ShaderProgram>(Detail::vertexShader, Detail::fragmentShader);
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "RenderThread", "Shader shader fragmentShader.");
} else {
isVertexShader = true;
videoShader_ =
std::make_unique<NativeXComponentSample::ShaderProgram>(Detail::vertexShader, Detail::fragmentShaderBW);
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "RenderThread", "Shader shader fragmentShaderBW.");
}
});
}
void RenderThread::Start()
{
if (running_) {
return;
}
running_ = true;
thread_ = std::thread([this]() {
ThreadMainLoop();
CleanGLResources();
DestroyNativeImage();
if (nativeWindow_ != nullptr) {
OH_NativeWindow_DestroyNativeWindow(nativeWindow_);
nativeWindow_ = nullptr;
}
if (encoderNativeWindow_ != nullptr) {
(void)OH_NativeWindow_NativeObjectUnreference(encoderNativeWindow_);
nativeWindow_ = nullptr;
}
DestroyRenderContext();
running_ = false;
});
}
void RenderThread::OnVsync(long long timestamp, void *data)
{
OH_LOG_Print(LOG_APP, LOG_DEBUG, LOG_PRINT_DOMAIN, "RenderThread", "OnVsync %{public}llu.", timestamp);
auto renderThread = static_cast<RenderThread *>(data);
if (renderThread == nullptr) {
return;
}
renderThread->vSyncCnt_++;
renderThread->wakeUpCond_.notify_one();
}
bool RenderThread::InitNativeVsync()
{
nativeVsync_ = OH_NativeVSync_Create(DEMO_NAME, strlen(DEMO_NAME));
if (nativeVsync_ == nullptr) {
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "RenderThread", "Create NativeVSync failed.");
return false;
}
(void)OH_NativeVSync_RequestFrame(nativeVsync_, &RenderThread::OnVsync, this);
return true;
}
void RenderThread::DestroyNativeVsync()
{
if (nativeVsync_ != nullptr) {
OH_NativeVSync_Destroy(nativeVsync_);
nativeVsync_ = nullptr;
}
}
void RenderThread::OnNativeImageFrameAvailable(void *data)
{
OH_LOG_Print(LOG_APP, LOG_DEBUG, LOG_PRINT_DOMAIN, "RenderThread", "OnNativeImageFrameAvailable.");
auto renderThread = static_cast<RenderThread *>(data);
if (renderThread == nullptr) {
return;
}
renderThread->availableFrameCnt_++;
renderThread->wakeUpCond_.notify_one();
}
bool RenderThread::CreateNativeImage()
{
nativeImage_ = OH_NativeImage_Create(-1, GL_TEXTURE_EXTERNAL_OES);
if (nativeImage_ == nullptr) {
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "RenderThread", "OH_NativeImage_Create failed.");
return false;
}
int ret = 0;
{
std::lock_guard<std::mutex> lock(nativeImageSurfaceIdMutex_);
nativeImageWindow_ = OH_NativeImage_AcquireNativeWindow(nativeImage_);
ret = OH_NativeImage_GetSurfaceId(nativeImage_, &nativeImageSurfaceId_);
}
if (ret != 0) {
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "RenderThread",
"OH_NativeImage_GetSurfaceId failed, ret is %{public}d.", ret);
return false;
}
nativeImageFrameAvailableListener_.context = this;
nativeImageFrameAvailableListener_.onFrameAvailable = &RenderThread::OnNativeImageFrameAvailable;
ret = OH_NativeImage_SetOnFrameAvailableListener(nativeImage_, nativeImageFrameAvailableListener_);
if (ret != 0) {
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "RenderThread",
"OH_NativeImage_SetOnFrameAvailableListener failed, ret is %{public}d.", ret);
return false;
}
return true;
}
void RenderThread::DestroyNativeImage()
{
if (nativeImageTexId_ != 0U) {
glDeleteTextures(1, &nativeImageTexId_);
nativeImageTexId_ = 0U;
}
if (nativeImage_ != nullptr) {
(void)OH_NativeImage_UnsetOnFrameAvailableListener(nativeImage_);
OH_NativeImage_Destroy(&nativeImage_);
nativeImage_ = nullptr;
}
}
void RenderThread::ThreadMainLoop()
{
threadId_ = std::this_thread::get_id();
if (!InitRenderContext()) {
return;
}
if (!CreateNativeImage()) {
return;
}
ConfigSceneMatrix();
while (running_) {
{
OH_LOG_Print(LOG_APP, LOG_DEBUG, LOG_PRINT_DOMAIN, "RenderThread", "Waiting for vsync.");
std::unique_lock<std::mutex> lock(wakeUpMutex_);
wakeUpCond_.wait(lock, [this]() { return wakeUp_ || availableFrameCnt_ > 0; });
wakeUp_ = false;
}
OH_LOG_Print(LOG_APP, LOG_DEBUG, LOG_PRINT_DOMAIN, "RenderThread", "Executing tasks.");
std::vector<RenderTask> tasks;
{
std::lock_guard<std::mutex> lock(taskMutex_);
tasks.swap(tasks_);
}
for (const auto &task : tasks) {
task(*renderContext_);
}
if (availableFrameCnt_ <= 0) {
continue;
}
DrawImage();
availableFrameCnt_--;
}
}
void RenderThread::PostTask(const RenderTask &task)
{
if (!running_) {
OH_LOG_Print(LOG_APP, LOG_WARN, LOG_PRINT_DOMAIN, "RenderThread",
"PostTask failed: RenderThread is not running");
return;
}
{
std::lock_guard<std::mutex> lock(taskMutex_);
tasks_.push_back(task);
}
if (std::this_thread::get_id() != threadId_) {
std::lock_guard<std::mutex> lock(wakeUpMutex_);
wakeUp_ = true;
wakeUpCond_.notify_one();
}
}
void RenderThread::ConfigSceneMatrix()
{
std::array<float, 16> translate_matrix = {
1.0, 0.0, 0.0, 0.5,
0.0, 1.0, 0.0, 0.5,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0
};
std::array<float, 16> scale_matrix = {
0.333, 0.0, 0.0, 0.0,
0.0, 0.25, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0
};
std::array<float, 16> rotate_matrix = {
-1.0, 0.0, 0.0, 0.0,
0.0, -1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0
};
std::array<float, 16> imageMatrixTmp;
std::array<float, 16> imageMatrix;
Detail::LeftProd(rotate_matrix.data(), scale_matrix.data(), imageMatrixTmp.data());
Detail::LeftProd(translate_matrix.data(), imageMatrixTmp.data(), imageMatrix.data());
OH_LOG_Print(LOG_APP, LOG_DEBUG, LOG_PRINT_DOMAIN, "RenderThread", "isCameraFront_:%{public}d.",
isCameraFront_.load());
drawImageMatrix_ = imageMatrix;
}
void RenderThread::DrawScene()
{
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
videoShader_->Use();
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_EXTERNAL_OES, nativeImageTexId_);
videoShader_->SetInt("tex", 0);
videoShader_->SetMatrix4v("matTransform", drawCameraImageMatrix_.data(), 16, false);
glBindVertexArray(vertexArrayObject_);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, RECTANGLE_INDICES);
imageShader_->Use();
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, noiseImageTexId_);
imageShader_->SetInt("tex", 0);
imageShader_->SetMatrix4v("matTransform", drawImageMatrix_.data(), 16, true);
glBindVertexArray(vertexArrayObject_);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, RECTANGLE_INDICES);
glFinish();
}
void RenderThread::UpdateImageResource(void* data, int32_t width, int32_t height, int32_t stride, bool mipmap)
{
glBindTexture(GL_TEXTURE_2D, noiseImageTexId_);
if (!data) {
constexpr uint32_t w = 32, h = 32;
constexpr uint32_t r = 720, g = 1023, b = 0, a = 3;
constexpr uint32_t mockColor = (a << 30) | (b << 20) | (g << 10) | r;
std::vector<uint32_t> data(w * h, mockColor);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB10_A2, w, h, 0, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV_EXT, data.data());
} else {
glPixelStorei(GL_UNPACK_ROW_LENGTH, stride >> 2);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB10_A2, width, height, 0,
GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV_EXT, data);
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
}
if (mipmap) {
glGenerateMipmap(GL_TEXTURE_2D);
}
glBindTexture(GL_TEXTURE_2D, 0);
}
void RenderThread::ImageDraw(OHNativeWindowBuffer *InBuffer, OHNativeWindowBuffer *OutBuffer,
int32_t imageWidth, int32_t imageHeight, int32_t viewWidth, int32_t viewHeight)
{
EGLImageKHR imgIn = renderContext_->CreateEGLImage(InBuffer);
EGLImageKHR imgOut = renderContext_->CreateEGLImage(OutBuffer);
glBindTexture(GL_TEXTURE_EXTERNAL_OES, inTexId_);
renderContext_->EGLImageTargetTexture2DOES(imgIn);
glBindTexture(GL_TEXTURE_EXTERNAL_OES, GL_NONE);
glBindFramebuffer(GL_FRAMEBUFFER, vertexFrameObject_);
glBindTexture(GL_TEXTURE_EXTERNAL_OES, outTexId_);
renderContext_->EGLImageTargetTexture2DOES(imgOut);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_EXTERNAL_OES, outTexId_, 0);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "RenderThread", "glCheckFramebufferStatus failed");
return;
}
glBindTexture(GL_TEXTURE_EXTERNAL_OES, GL_NONE);
float imageAspect = (float) imageWidth / imageHeight;
float viewAspect = (float) viewWidth / viewHeight;
int viewportX, viewportY, viewportWidth, viewportHeight;
if (imageAspect > viewAspect) {
viewportHeight = viewHeight;
viewportWidth = static_cast<int>(viewportHeight * imageAspect);
viewportX = -(viewportWidth - viewWidth) / 2;
viewportY = 0;
} else {
viewportWidth = viewWidth;
viewportHeight = static_cast<int> (viewWidth / imageAspect);
viewportX = 0;
viewportY = -(viewportHeight - viewHeight) / 2;
}
OH_LOG_Print(
LOG_APP, LOG_DEBUG, LOG_PRINT_DOMAIN, "RenderThread",
"DrawImagefhm. %{public}d %{public}d %{public}d %{public}d => %{public}d %{public}d %{public}d %{public}d",
imageWidth, imageHeight, viewWidth, viewHeight, viewportX, viewportY, viewportWidth, viewportHeight);
glClearColor(0, 0, 0, 0);
glClear(GL_COLOR_BUFFER_BIT);
glViewport(viewportX, viewportY, viewportWidth, viewportHeight);
frameShader_->Use();
frameShader_->SetMatrix4v("matTransform", drawCameraImageMatrix_.data(), 16, false);
glBindVertexArray(vertexArrayObject_);
glBindTexture(GL_TEXTURE_EXTERNAL_OES, inTexId_);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, Detail::indices);
glBindTexture(GL_TEXTURE_EXTERNAL_OES, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
glUseProgram(0);
glFinish();
renderContext_->DeleteEGLImage(imgIn);
renderContext_->DeleteEGLImage(imgOut);
}
void RenderThread::DrawImage()
{
OH_LOG_Print(LOG_APP, LOG_DEBUG, LOG_PRINT_DOMAIN, "RenderThread", "DrawImage.");
if (eglSurface_ == EGL_NO_SURFACE) {
OH_LOG_Print(LOG_APP, LOG_WARN, LOG_PRINT_DOMAIN, "RenderThread", "eglSurface_ is EGL_NO_SURFACE");
return;
}
OHNativeWindowBuffer *InBuffer;
int32_t fenceFd1 = -1;
int32_t ret = OH_NativeImage_AcquireNativeWindowBuffer(nativeImage_, &InBuffer, &fenceFd1);
if (ret != 0) {
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "RenderThread",
"OH_NativeImage_AcquireNativeWindowBuffer failed, ret: %{public}d", ret);
return;
}
if (OH_NativeImage_GetTransformMatrixV2(nativeImage_, drawCameraImageMatrix_.data())) {
OH_LOG_Print(LOG_APP, LOG_WARN, LOG_PRINT_DOMAIN,
"RenderThread", "OH_NativeImage_GetTransformMatrix failed!");
return;
}
ret = OH_NativeWindow_NativeObjectReference(InBuffer);
if (ret != 0) {
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "RenderThread",
"OH_NativeWindow_NativeObjectReference failed, ret: %{public}d", ret);
return;
}
OHNativeWindowBuffer *OutBuffer;
OHNativeWindowBuffer *OutBufferEncoder;
int32_t fenceFd2 = -1;
int32_t fenceFd3 = -1;
ret = OH_NativeWindow_NativeWindowRequestBuffer(nativeWindow_, &OutBuffer, &fenceFd2);
if (ret != 0) {
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "RenderThread",
"OH_NativeWindow_NativeWindowRequestBuffer failed, ret: %{public}d", ret);
return;
}
int retCode = -1;
uint32_t timeout = 3000;
if (fenceFd2 != -1) {
struct pollfd pollfds = {0};
pollfds.fd = fenceFd2;
pollfds.events = POLLIN;
do {
retCode = poll(&pollfds, 1, timeout);
} while (retCode == -1 && (errno == EINTR || errno == EAGAIN));
close(fenceFd2);
}
ret = OH_NativeWindow_NativeWindowRequestBuffer(encoderNativeWindow_, &OutBufferEncoder, &fenceFd3);
if (ret != 0) {
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "RenderThread",
"OH_NativeWindow_NativeWindowRequestBuffer failed, ret: %{public}d", ret);
return;
}
if (fenceFd3 != -1) {
struct pollfd pollfds = {0};
pollfds.fd = fenceFd3;
pollfds.events = POLLIN;
do {
retCode = poll(&pollfds, 1, timeout);
} while (retCode == -1 && (errno == EINTR || errno == EAGAIN));
close(fenceFd3);
}
int viewWidth = 0;
int viewHeight = 0;
OH_NativeWindow_NativeWindowHandleOpt(encoderNativeWindow_, GET_BUFFER_GEOMETRY, &viewHeight, &viewWidth);
OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "RenderThread", "viewHeight:%{public}d, viewWidth:%{public}d",
viewHeight, viewWidth);
ret = OH_NativeWindow_NativeWindowHandleOpt(xcomponentWindows_, GET_BUFFER_GEOMETRY, &xcomponentHeight_,
&xcomponentWidth_);
if (ret != 0 || xcomponentHeight_ == 0 || xcomponentWidth_ == 0) {
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "RenderThread",
"OH_NativeWindow_NativeWindowHandleOpt get Xcomponent size failed!");
}
OH_LOG_Print(LOG_APP, LOG_DEBUG, LOG_PRINT_DOMAIN, "RenderThread",
"xcomponentHeight_:%{public}d, xcomponentWidth_:%{public}d", xcomponentHeight_, xcomponentWidth_);
int imageRotation = cameraRotation_;
if (imageRotation == 0 || imageRotation == 180) {
ImageDraw(InBuffer, OutBuffer, viewHeight, viewWidth, xcomponentWidth_, xcomponentHeight_);
ImageDraw(InBuffer, OutBufferEncoder, viewHeight, viewWidth, viewWidth, viewHeight);
} else {
ImageDraw(InBuffer, OutBuffer, viewWidth, viewHeight, xcomponentWidth_, xcomponentHeight_);
ImageDraw(InBuffer, OutBufferEncoder, viewWidth, viewHeight, viewWidth, viewHeight);
}
OH_NativeWindow_NativeObjectUnreference(InBuffer);
OH_NativeImage_ReleaseNativeWindowBuffer(nativeImage_, InBuffer, fenceFd1);
Region region{nullptr, 0};
int acquireFenceFd1 = -1;
int acquireFenceFd2 = -1;
OH_NativeWindow_NativeWindowFlushBuffer(nativeWindow_, OutBuffer, acquireFenceFd1, region);
OH_NativeWindow_NativeWindowFlushBuffer(encoderNativeWindow_, OutBufferEncoder, acquireFenceFd2, region);
}
OH_NativeImage *RenderThread::GetNativeImageEncoder()
{
return nativeImage_;
}
void RenderThread::SetCameraFront(bool isCameraFront)
{
isCameraFront_ = isCameraFront;
}
void RenderThread::UpdateCameraRotation(int rotation)
{
cameraRotation_ = rotation;
}
}