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