blob: cde8b89530d5d52aa008cc922734d95b44d7ac16 [file] [log] [blame]
Chih-Chung Chang43fcc392011-08-02 16:17:39 +08001/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "NativeWindowRenderer"
18#include "NativeWindowRenderer.h"
19
20#include <GLES2/gl2.h>
21#include <GLES2/gl2ext.h>
22#include <cutils/log.h>
23#include <gui/SurfaceTexture.h>
24#include <gui/SurfaceTextureClient.h>
25#include <stagefright/MediaBuffer.h>
26#include <stagefright/MediaDebug.h>
27#include <stagefright/MetaData.h>
28#include <surfaceflinger/Surface.h>
29#include "VideoEditorTools.h"
30
31#define CHECK_EGL_ERROR CHECK(EGL_SUCCESS == eglGetError())
32#define CHECK_GL_ERROR CHECK(GLenum(GL_NO_ERROR) == glGetError())
33
34//
35// Vertex and fragment programs
36//
37
38// The matrix is derived from
39// frameworks/base/media/libstagefright/colorconversion/ColorConverter.cpp
40//
41// R * 255 = 1.164 * (Y - 16) + 1.596 * (V - 128)
42// G * 255 = 1.164 * (Y - 16) - 0.813 * (V - 128) - 0.391 * (U - 128)
43// B * 255 = 1.164 * (Y - 16) + 2.018 * (U - 128)
44//
45// Here we assume YUV are in the range of [0,255], RGB are in the range of
46// [0, 1]
47#define RGB2YUV_MATRIX \
48"const mat4 rgb2yuv = mat4("\
49" 65.52255, -37.79398, 111.98732, 0.00000,"\
50" 128.62729, -74.19334, -93.81088, 0.00000,"\
51" 24.92233, 111.98732, -18.17644, 0.00000,"\
52" 16.00000, 128.00000, 128.00000, 1.00000);\n"
53
54#define YUV2RGB_MATRIX \
55"const mat4 yuv2rgb = mat4("\
56" 0.00456, 0.00456, 0.00456, 0.00000,"\
57" 0.00000, -0.00153, 0.00791, 0.00000,"\
58" 0.00626, -0.00319, 0.00000, 0.00000,"\
59" -0.87416, 0.53133, -1.08599, 1.00000);\n"
60
61static const char vSrcNormal[] =
62 "attribute vec4 vPosition;\n"
63 "attribute vec2 vTexPos;\n"
64 "uniform mat4 texMatrix;\n"
65 "varying vec2 texCoords;\n"
66 "varying float topDown;\n"
67 "void main() {\n"
68 " gl_Position = vPosition;\n"
69 " texCoords = (texMatrix * vec4(vTexPos, 0.0, 1.0)).xy;\n"
70 " topDown = vTexPos.y;\n"
71 "}\n";
72
73static const char fSrcNormal[] =
74 "#extension GL_OES_EGL_image_external : require\n"
75 "precision mediump float;\n"
76 "uniform samplerExternalOES texSampler;\n"
77 "varying vec2 texCoords;\n"
78 "void main() {\n"
79 " gl_FragColor = texture2D(texSampler, texCoords);\n"
80 "}\n";
81
82static const char fSrcSepia[] =
83 "#extension GL_OES_EGL_image_external : require\n"
84 "precision mediump float;\n"
85 "uniform samplerExternalOES texSampler;\n"
86 "varying vec2 texCoords;\n"
87 RGB2YUV_MATRIX
88 YUV2RGB_MATRIX
89 "void main() {\n"
90 " vec4 rgb = texture2D(texSampler, texCoords);\n"
91 " vec4 yuv = rgb2yuv * rgb;\n"
92 " yuv = vec4(yuv.x, 117.0, 139.0, 1.0);\n"
93 " gl_FragColor = yuv2rgb * yuv;\n"
94 "}\n";
95
96static const char fSrcNegative[] =
97 "#extension GL_OES_EGL_image_external : require\n"
98 "precision mediump float;\n"
99 "uniform samplerExternalOES texSampler;\n"
100 "varying vec2 texCoords;\n"
101 RGB2YUV_MATRIX
102 YUV2RGB_MATRIX
103 "void main() {\n"
104 " vec4 rgb = texture2D(texSampler, texCoords);\n"
105 " vec4 yuv = rgb2yuv * rgb;\n"
106 " yuv = vec4(255.0 - yuv.x, yuv.y, yuv.z, 1.0);\n"
107 " gl_FragColor = yuv2rgb * yuv;\n"
108 "}\n";
109
110static const char fSrcGradient[] =
111 "#extension GL_OES_EGL_image_external : require\n"
112 "precision mediump float;\n"
113 "uniform samplerExternalOES texSampler;\n"
114 "varying vec2 texCoords;\n"
115 "varying float topDown;\n"
116 RGB2YUV_MATRIX
117 YUV2RGB_MATRIX
118 "void main() {\n"
119 " vec4 rgb = texture2D(texSampler, texCoords);\n"
120 " vec4 yuv = rgb2yuv * rgb;\n"
121 " vec4 mixin = vec4(15.0/31.0, 59.0/63.0, 31.0/31.0, 1.0);\n"
122 " vec4 yuv2 = rgb2yuv * vec4((mixin.xyz * topDown), 1);\n"
123 " yuv = vec4(yuv.x, yuv2.y, yuv2.z, 1);\n"
124 " gl_FragColor = yuv2rgb * yuv;\n"
125 "}\n";
126
127namespace android {
128
129NativeWindowRenderer::NativeWindowRenderer(sp<ANativeWindow> nativeWindow,
130 int width, int height)
131 : mNativeWindow(nativeWindow)
132 , mDstWidth(width)
133 , mDstHeight(height)
134 , mLastVideoEffect(-1)
135 , mNextTextureId(100)
136 , mActiveInputs(0)
137 , mThreadCmd(CMD_IDLE) {
138 createThread(threadStart, this);
139}
140
141// The functions below run in the GL thread.
142//
143// All GL-related work is done in this thread, and other threads send
144// requests to this thread using a command code. We expect most of the
145// time there will only be one thread sending in requests, so we let
146// other threads wait until the request is finished by GL thread.
147
148int NativeWindowRenderer::threadStart(void* self) {
149 LOGD("create thread");
150 ((NativeWindowRenderer*)self)->glThread();
151 return 0;
152}
153
154void NativeWindowRenderer::glThread() {
155 initializeEGL();
156 createPrograms();
157
158 Mutex::Autolock autoLock(mLock);
159 bool quit = false;
160 while (!quit) {
161 switch (mThreadCmd) {
162 case CMD_IDLE:
163 mCond.wait(mLock);
164 continue;
165 case CMD_RENDER_INPUT:
166 render(mThreadRenderInput);
167 break;
168 case CMD_RESERVE_TEXTURE:
169 glBindTexture(GL_TEXTURE_EXTERNAL_OES, mThreadTextureId);
170 CHECK_GL_ERROR;
171 break;
172 case CMD_DELETE_TEXTURE:
173 glDeleteTextures(1, &mThreadTextureId);
174 break;
175 case CMD_QUIT:
176 terminateEGL();
177 quit = true;
178 break;
179 }
180 // Tell the requester that the command is finished.
181 mThreadCmd = CMD_IDLE;
182 mCond.broadcast();
183 }
184 LOGD("quit");
185}
186
187void NativeWindowRenderer::initializeEGL() {
188 mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
189 CHECK_EGL_ERROR;
190
191 EGLint majorVersion;
192 EGLint minorVersion;
193 eglInitialize(mEglDisplay, &majorVersion, &minorVersion);
194 CHECK_EGL_ERROR;
195
196 EGLConfig config;
197 EGLint numConfigs = -1;
198 EGLint configAttribs[] = {
199 EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
200 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
201 EGL_RED_SIZE, 8,
202 EGL_GREEN_SIZE, 8,
203 EGL_BLUE_SIZE, 8,
204 EGL_NONE
205 };
206 eglChooseConfig(mEglDisplay, configAttribs, &config, 1, &numConfigs);
207 CHECK_EGL_ERROR;
208
209 mEglSurface = eglCreateWindowSurface(mEglDisplay, config,
210 mNativeWindow.get(), NULL);
211 CHECK_EGL_ERROR;
212
213 EGLint contextAttribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
214 mEglContext = eglCreateContext(mEglDisplay, config, EGL_NO_CONTEXT,
215 contextAttribs);
216 CHECK_EGL_ERROR;
217
218 eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext);
219 CHECK_EGL_ERROR;
220}
221
222void NativeWindowRenderer::terminateEGL() {
223 eglDestroyContext(mEglDisplay, mEglContext);
224 eglDestroySurface(mEglDisplay, mEglSurface);
225 eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
226 eglTerminate(mEglDisplay);
227}
228
229void NativeWindowRenderer::createPrograms() {
230 GLuint vShader;
231 loadShader(GL_VERTEX_SHADER, vSrcNormal, &vShader);
232
233 const char* fSrc[NUMBER_OF_EFFECTS] = {
234 fSrcNormal, fSrcSepia, fSrcNegative, fSrcGradient
235 };
236
237 for (int i = 0; i < NUMBER_OF_EFFECTS; i++) {
238 GLuint fShader;
239 loadShader(GL_FRAGMENT_SHADER, fSrc[i], &fShader);
240 createProgram(vShader, fShader, &mProgram[i]);
241 glDeleteShader(fShader);
242 CHECK_GL_ERROR;
243 }
244
245 glDeleteShader(vShader);
246 CHECK_GL_ERROR;
247}
248
249void NativeWindowRenderer::createProgram(
250 GLuint vertexShader, GLuint fragmentShader, GLuint* outPgm) {
251
252 GLuint program = glCreateProgram();
253 CHECK_GL_ERROR;
254
255 glAttachShader(program, vertexShader);
256 CHECK_GL_ERROR;
257
258 glAttachShader(program, fragmentShader);
259 CHECK_GL_ERROR;
260
261 glLinkProgram(program);
262 CHECK_GL_ERROR;
263
264 GLint linkStatus = GL_FALSE;
265 glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
266 if (linkStatus != GL_TRUE) {
267 GLint infoLen = 0;
268 glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLen);
269 if (infoLen) {
270 char* buf = (char*) malloc(infoLen);
271 if (buf) {
272 glGetProgramInfoLog(program, infoLen, NULL, buf);
273 LOGE("Program link log:\n%s\n", buf);
274 free(buf);
275 }
276 }
277 glDeleteProgram(program);
278 program = 0;
279 }
280
281 *outPgm = program;
282}
283
284void NativeWindowRenderer::loadShader(GLenum shaderType, const char* pSource,
285 GLuint* outShader) {
286 GLuint shader = glCreateShader(shaderType);
287 CHECK_GL_ERROR;
288
289 glShaderSource(shader, 1, &pSource, NULL);
290 CHECK_GL_ERROR;
291
292 glCompileShader(shader);
293 CHECK_GL_ERROR;
294
295 GLint compiled = 0;
296 glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
297 if (!compiled) {
298 GLint infoLen = 0;
299 glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
300 char* buf = (char*) malloc(infoLen);
301 if (buf) {
302 glGetShaderInfoLog(shader, infoLen, NULL, buf);
303 LOGE("Shader compile log:\n%s\n", buf);
304 free(buf);
305 }
306 glDeleteShader(shader);
307 shader = 0;
308 }
309 *outShader = shader;
310}
311
312NativeWindowRenderer::~NativeWindowRenderer() {
313 CHECK(mActiveInputs == 0);
314 startRequest(CMD_QUIT);
315 sendRequest();
316}
317
318void NativeWindowRenderer::render(RenderInput* input) {
319 sp<SurfaceTexture> ST = input->mST;
320 sp<SurfaceTextureClient> STC = input->mSTC;
321
322 if (input->mIsExternalBuffer) {
323 queueExternalBuffer(STC.get(), input->mBuffer,
324 input->mWidth, input->mHeight);
325 } else {
326 queueInternalBuffer(STC.get(), input->mBuffer);
327 }
328
329 ST->updateTexImage();
330 glClearColor(0, 0, 0, 0);
331 glClear(GL_COLOR_BUFFER_BIT);
332
333 calculatePositionCoordinates(input->mRenderingMode,
334 input->mWidth, input->mHeight);
335
336 const GLfloat textureCoordinates[] = {
337 0.0f, 1.0f,
338 0.0f, 0.0f,
339 1.0f, 0.0f,
340 1.0f, 1.0f,
341 };
342
343 updateProgramAndHandle(input->mVideoEffect);
344
345 glVertexAttribPointer(mPositionHandle, 2, GL_FLOAT, GL_FALSE, 0,
346 mPositionCoordinates);
347 CHECK_GL_ERROR;
348
349 glEnableVertexAttribArray(mPositionHandle);
350 CHECK_GL_ERROR;
351
352 glVertexAttribPointer(mTexPosHandle, 2, GL_FLOAT, GL_FALSE, 0,
353 textureCoordinates);
354 CHECK_GL_ERROR;
355
356 glEnableVertexAttribArray(mTexPosHandle);
357 CHECK_GL_ERROR;
358
359 GLfloat texMatrix[16];
360 ST->getTransformMatrix(texMatrix);
361 glUniformMatrix4fv(mTexMatrixHandle, 1, GL_FALSE, texMatrix);
362 CHECK_GL_ERROR;
363
364 glBindTexture(GL_TEXTURE_EXTERNAL_OES, input->mTextureId);
365 CHECK_GL_ERROR;
366
367 glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
368 glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
369 glTexParameteri(
370 GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
371 glTexParameteri(
372 GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
373 CHECK_GL_ERROR;
374
375 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
376 CHECK_GL_ERROR;
377
378 eglSwapBuffers(mEglDisplay, mEglSurface);
379}
380
381void NativeWindowRenderer::queueInternalBuffer(ANativeWindow *anw,
382 MediaBuffer* buffer) {
383 int64_t timeUs;
384 CHECK(buffer->meta_data()->findInt64(kKeyTime, &timeUs));
385 native_window_set_buffers_timestamp(anw, timeUs * 1000);
386 status_t err = anw->queueBuffer(anw, buffer->graphicBuffer().get());
387 if (err != 0) {
388 LOGE("queueBuffer failed with error %s (%d)", strerror(-err), -err);
389 return;
390 }
391
392 sp<MetaData> metaData = buffer->meta_data();
393 metaData->setInt32(kKeyRendered, 1);
394}
395
396void NativeWindowRenderer::queueExternalBuffer(ANativeWindow* anw,
397 MediaBuffer* buffer, int width, int height) {
398 native_window_set_buffers_geometry(anw, width, height,
399 HAL_PIXEL_FORMAT_YV12);
400 native_window_set_usage(anw, GRALLOC_USAGE_SW_WRITE_OFTEN);
401
402 ANativeWindowBuffer* anb;
403 anw->dequeueBuffer(anw, &anb);
404 CHECK(anb != NULL);
405
406 sp<GraphicBuffer> buf(new GraphicBuffer(anb, false));
407 CHECK(NO_ERROR == anw->lockBuffer(anw, buf->getNativeBuffer()));
408
409 // Copy the buffer
410 uint8_t* img = NULL;
411 buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img));
412 copyYV12Buffer(buffer, img, width, height, buf->getStride());
413 buf->unlock();
414 CHECK(NO_ERROR == anw->queueBuffer(anw, buf->getNativeBuffer()));
415}
416
417void NativeWindowRenderer::copyYV12Buffer(MediaBuffer* src, uint8_t* dst,
418 int srcWidth, int srcHeight, int stride) {
419 int strideUV = (stride / 2 + 0xf) & ~0xf;
420 uint8_t* p = (uint8_t*)src->data() + src->range_offset();
421 // Y
422 for (int i = srcHeight; i > 0; i--) {
423 memcpy(dst, p, srcWidth);
424 dst += stride;
425 p += srcWidth;
426 }
427 // The src is I420, the dst is YV12.
428 // U
429 p += srcWidth * srcHeight / 4;
430 for (int i = srcHeight / 2; i > 0; i--) {
431 memcpy(dst, p, srcWidth / 2);
432 dst += strideUV;
433 p += srcWidth / 2;
434 }
435 // V
436 p -= srcWidth * srcHeight / 2;
437 for (int i = srcHeight / 2; i > 0; i--) {
438 memcpy(dst, p, srcWidth / 2);
439 dst += strideUV;
440 p += srcWidth / 2;
441 }
442}
443
444void NativeWindowRenderer::updateProgramAndHandle(uint32_t videoEffect) {
445 if (mLastVideoEffect == videoEffect) {
446 return;
447 }
448
449 mLastVideoEffect = videoEffect;
450 int i;
451 switch (mLastVideoEffect) {
452 case VIDEO_EFFECT_NONE:
453 i = 0;
454 break;
455 case VIDEO_EFFECT_SEPIA:
456 i = 1;
457 break;
458 case VIDEO_EFFECT_NEGATIVE:
459 i = 2;
460 break;
461 case VIDEO_EFFECT_GRADIENT:
462 i = 3;
463 break;
464 default:
465 i = 0;
466 break;
467 }
468 glUseProgram(mProgram[i]);
469 CHECK_GL_ERROR;
470
471 mPositionHandle = glGetAttribLocation(mProgram[i], "vPosition");
472 mTexPosHandle = glGetAttribLocation(mProgram[i], "vTexPos");
473 mTexMatrixHandle = glGetUniformLocation(mProgram[i], "texMatrix");
474 CHECK_GL_ERROR;
475}
476
477void NativeWindowRenderer::calculatePositionCoordinates(
478 M4xVSS_MediaRendering renderingMode, int srcWidth, int srcHeight) {
479 float x, y;
480 switch (renderingMode) {
481 case M4xVSS_kResizing:
482 default:
483 x = 1;
484 y = 1;
485 break;
486 case M4xVSS_kCropping:
487 x = float(srcWidth) / mDstWidth;
488 y = float(srcHeight) / mDstHeight;
489 // Make the smaller side 1
490 if (x > y) {
491 x /= y;
492 y = 1;
493 } else {
494 y /= x;
495 x = 1;
496 }
497 break;
498 case M4xVSS_kBlackBorders:
499 x = float(srcWidth) / mDstWidth;
500 y = float(srcHeight) / mDstHeight;
501 // Make the larger side 1
502 if (x > y) {
503 y /= x;
504 x = 1;
505 } else {
506 x /= y;
507 y = 1;
508 }
509 break;
510 }
511
512 mPositionCoordinates[0] = -x;
513 mPositionCoordinates[1] = y;
514 mPositionCoordinates[2] = -x;
515 mPositionCoordinates[3] = -y;
516 mPositionCoordinates[4] = x;
517 mPositionCoordinates[5] = -y;
518 mPositionCoordinates[6] = x;
519 mPositionCoordinates[7] = y;
520}
521
522//
523// The functions below run in other threads.
524//
525
526void NativeWindowRenderer::startRequest(int cmd) {
527 mLock.lock();
528 while (mThreadCmd != CMD_IDLE) {
529 mCond.wait(mLock);
530 }
531 mThreadCmd = cmd;
532}
533
534void NativeWindowRenderer::sendRequest() {
535 mCond.broadcast();
536 while (mThreadCmd != CMD_IDLE) {
537 mCond.wait(mLock);
538 }
539 mLock.unlock();
540}
541
542RenderInput* NativeWindowRenderer::createRenderInput() {
543 LOGD("new render input %d", mNextTextureId);
544 RenderInput* input = new RenderInput(this, mNextTextureId);
545
546 startRequest(CMD_RESERVE_TEXTURE);
547 mThreadTextureId = mNextTextureId;
548 sendRequest();
549
550 mNextTextureId++;
551 mActiveInputs++;
552 return input;
553}
554
555void NativeWindowRenderer::destroyRenderInput(RenderInput* input) {
556 LOGD("destroy render input %d", input->mTextureId);
557 GLuint textureId = input->mTextureId;
558 delete input;
559
560 startRequest(CMD_DELETE_TEXTURE);
561 mThreadTextureId = textureId;
562 sendRequest();
563
564 mActiveInputs--;
565}
566
567//
568// RenderInput
569//
570
571RenderInput::RenderInput(NativeWindowRenderer* renderer, GLuint textureId)
572 : mRenderer(renderer)
573 , mTextureId(textureId) {
574 mST = new SurfaceTexture(mTextureId);
575 mSTC = new SurfaceTextureClient(mST);
576}
577
578RenderInput::~RenderInput() {
579}
580
581ANativeWindow* RenderInput::getTargetWindow() {
582 return mSTC.get();
583}
584
585void RenderInput::updateVideoSize(sp<MetaData> meta) {
586 CHECK(meta->findInt32(kKeyWidth, &mWidth));
587 CHECK(meta->findInt32(kKeyHeight, &mHeight));
588
589 int left, top, right, bottom;
590 if (meta->findRect(kKeyCropRect, &left, &top, &right, &bottom)) {
591 mWidth = right - left + 1;
592 mHeight = bottom - top + 1;
593 }
594
595 // If rotation degrees is 90 or 270, swap width and height
596 // (mWidth and mHeight are the _rotated_ source rectangle).
597 int32_t rotationDegrees;
598 if (!meta->findInt32(kKeyRotation, &rotationDegrees)) {
599 rotationDegrees = 0;
600 }
601
602 if (rotationDegrees == 90 || rotationDegrees == 270) {
603 int tmp = mWidth;
604 mWidth = mHeight;
605 mHeight = tmp;
606 }
607}
608
609void RenderInput::render(MediaBuffer* buffer, uint32_t videoEffect,
610 M4xVSS_MediaRendering renderingMode, bool isExternalBuffer) {
611 mVideoEffect = videoEffect;
612 mRenderingMode = renderingMode;
613 mIsExternalBuffer = isExternalBuffer;
614 mBuffer = buffer;
615
616 mRenderer->startRequest(NativeWindowRenderer::CMD_RENDER_INPUT);
617 mRenderer->mThreadRenderInput = this;
618 mRenderer->sendRequest();
619}
620
621} // namespace android