Home | History | Annotate | Download | only in lvpp
      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 
    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 
    148 int NativeWindowRenderer::threadStart(void* self) {
    149     LOGD("create thread");
    150     ((NativeWindowRenderer*)self)->glThread();
    151     return 0;
    152 }
    153 
    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 
    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 
    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 
    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 
    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 
    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 
    312 NativeWindowRenderer::~NativeWindowRenderer() {
    313     CHECK(mActiveInputs == 0);
    314     startRequest(CMD_QUIT);
    315     sendRequest();
    316 }
    317 
    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 
    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 
    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 
    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 
    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 
    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 
    526 void NativeWindowRenderer::startRequest(int cmd) {
    527     mLock.lock();
    528     while (mThreadCmd != CMD_IDLE) {
    529         mCond.wait(mLock);
    530     }
    531     mThreadCmd = cmd;
    532 }
    533 
    534 void NativeWindowRenderer::sendRequest() {
    535     mCond.broadcast();
    536     while (mThreadCmd != CMD_IDLE) {
    537         mCond.wait(mLock);
    538     }
    539     mLock.unlock();
    540 }
    541 
    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 
    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 
    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 
    581 RenderInput::~RenderInput() {
    582 }
    583 
    584 ANativeWindow* RenderInput::getTargetWindow() {
    585     return mSTC.get();
    586 }
    587 
    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 
    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