• 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 <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 
61 static 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 
73 static 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 
82 static 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 
96 static 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 
110 static 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 
127 namespace android {
128 
NativeWindowRenderer(sp<ANativeWindow> nativeWindow,int width,int height)129 NativeWindowRenderer::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 
threadStart(void * self)148 int NativeWindowRenderer::threadStart(void* self) {
149     LOGD("create thread");
150     ((NativeWindowRenderer*)self)->glThread();
151     return 0;
152 }
153 
glThread()154 void 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 
initializeEGL()187 void 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 
terminateEGL()222 void 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 
createPrograms()229 void 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 
createProgram(GLuint vertexShader,GLuint fragmentShader,GLuint * outPgm)249 void 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 
loadShader(GLenum shaderType,const char * pSource,GLuint * outShader)284 void 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 
~NativeWindowRenderer()312 NativeWindowRenderer::~NativeWindowRenderer() {
313     CHECK(mActiveInputs == 0);
314     startRequest(CMD_QUIT);
315     sendRequest();
316 }
317 
render(RenderInput * input)318 void 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 
queueInternalBuffer(ANativeWindow * anw,MediaBuffer * buffer)381 void 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 
queueExternalBuffer(ANativeWindow * anw,MediaBuffer * buffer,int width,int height)396 void 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     copyI420Buffer(buffer, img, width, height, buf->getStride());
413     buf->unlock();
414     CHECK(NO_ERROR == anw->queueBuffer(anw, buf->getNativeBuffer()));
415 }
416 
copyI420Buffer(MediaBuffer * src,uint8_t * dst,int srcWidth,int srcHeight,int stride)417 void NativeWindowRenderer::copyI420Buffer(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 
updateProgramAndHandle(uint32_t videoEffect)444 void 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 
calculatePositionCoordinates(M4xVSS_MediaRendering renderingMode,int srcWidth,int srcHeight)477 void 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 
startRequest(int cmd)526 void NativeWindowRenderer::startRequest(int cmd) {
527     mLock.lock();
528     while (mThreadCmd != CMD_IDLE) {
529         mCond.wait(mLock);
530     }
531     mThreadCmd = cmd;
532 }
533 
sendRequest()534 void NativeWindowRenderer::sendRequest() {
535     mCond.broadcast();
536     while (mThreadCmd != CMD_IDLE) {
537         mCond.wait(mLock);
538     }
539     mLock.unlock();
540 }
541 
createRenderInput()542 RenderInput* 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 
destroyRenderInput(RenderInput * input)555 void 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 
RenderInput(NativeWindowRenderer * renderer,GLuint textureId)571 RenderInput::RenderInput(NativeWindowRenderer* renderer, GLuint textureId)
572     : mRenderer(renderer)
573     , mTextureId(textureId) {
574     mST = new SurfaceTexture(mTextureId);
575     uint32_t outWidth, outHeight, outTransform;
576     mST->connect(NATIVE_WINDOW_API_MEDIA, &outWidth, &outHeight, &outTransform);
577 
578     mSTC = new SurfaceTextureClient(mST);
579 }
580 
~RenderInput()581 RenderInput::~RenderInput() {
582 }
583 
getTargetWindow()584 ANativeWindow* RenderInput::getTargetWindow() {
585     return mSTC.get();
586 }
587 
updateVideoSize(sp<MetaData> meta)588 void RenderInput::updateVideoSize(sp<MetaData> meta) {
589     CHECK(meta->findInt32(kKeyWidth, &mWidth));
590     CHECK(meta->findInt32(kKeyHeight, &mHeight));
591 
592     int left, top, right, bottom;
593     if (meta->findRect(kKeyCropRect, &left, &top, &right, &bottom)) {
594         mWidth = right - left + 1;
595         mHeight = bottom - top + 1;
596     }
597 
598     // If rotation degrees is 90 or 270, swap width and height
599     // (mWidth and mHeight are the _rotated_ source rectangle).
600     int32_t rotationDegrees;
601     if (!meta->findInt32(kKeyRotation, &rotationDegrees)) {
602         rotationDegrees = 0;
603     }
604 
605     if (rotationDegrees == 90 || rotationDegrees == 270) {
606         int tmp = mWidth;
607         mWidth = mHeight;
608         mHeight = tmp;
609     }
610 }
611 
render(MediaBuffer * buffer,uint32_t videoEffect,M4xVSS_MediaRendering renderingMode,bool isExternalBuffer)612 void RenderInput::render(MediaBuffer* buffer, uint32_t videoEffect,
613         M4xVSS_MediaRendering renderingMode, bool isExternalBuffer) {
614     mVideoEffect = videoEffect;
615     mRenderingMode = renderingMode;
616     mIsExternalBuffer = isExternalBuffer;
617     mBuffer = buffer;
618 
619     mRenderer->startRequest(NativeWindowRenderer::CMD_RENDER_INPUT);
620     mRenderer->mThreadRenderInput = this;
621     mRenderer->sendRequest();
622 }
623 
624 }  // namespace android
625