Support post-decode video rotation.

Change-Id: Ia371316e73a57e44610de86adce3eaa560afbf84
diff --git a/include/media/IOMX.h b/include/media/IOMX.h
index 2f61cbe..f794766 100644
--- a/include/media/IOMX.h
+++ b/include/media/IOMX.h
@@ -115,7 +115,8 @@
             const char *componentName,
             OMX_COLOR_FORMATTYPE colorFormat,
             size_t encodedWidth, size_t encodedHeight,
-            size_t displayWidth, size_t displayHeight) = 0;
+            size_t displayWidth, size_t displayHeight,
+            int32_t rotationDegrees) = 0;
 
     // Note: These methods are _not_ virtual, it exists as a wrapper around
     // the virtual "createRenderer" method above facilitating extraction
@@ -125,14 +126,16 @@
             const char *componentName,
             OMX_COLOR_FORMATTYPE colorFormat,
             size_t encodedWidth, size_t encodedHeight,
-            size_t displayWidth, size_t displayHeight);
+            size_t displayWidth, size_t displayHeight,
+            int32_t rotationDegrees);
 
     sp<IOMXRenderer> createRendererFromJavaSurface(
             JNIEnv *env, jobject javaSurface,
             const char *componentName,
             OMX_COLOR_FORMATTYPE colorFormat,
             size_t encodedWidth, size_t encodedHeight,
-            size_t displayWidth, size_t displayHeight);
+            size_t displayWidth, size_t displayHeight,
+            int32_t rotationDegrees);
 };
 
 struct omx_message {
diff --git a/include/media/stagefright/HardwareAPI.h b/include/media/stagefright/HardwareAPI.h
index 221c679..63f11d1 100644
--- a/include/media/stagefright/HardwareAPI.h
+++ b/include/media/stagefright/HardwareAPI.h
@@ -32,6 +32,14 @@
         size_t displayWidth, size_t displayHeight,
         size_t decodedWidth, size_t decodedHeight);
 
+extern android::VideoRenderer *createRendererWithRotation(
+        const android::sp<android::ISurface> &surface,
+        const char *componentName,
+        OMX_COLOR_FORMATTYPE colorFormat,
+        size_t displayWidth, size_t displayHeight,
+        size_t decodedWidth, size_t decodedHeight,
+        int32_t rotationDegrees);
+
 extern android::OMXPluginBase *createOMXPlugin();
 
 #endif  // HARDWARE_API_H_
diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h
index cffcef2..29bfc4a 100644
--- a/include/media/stagefright/MetaData.h
+++ b/include/media/stagefright/MetaData.h
@@ -32,6 +32,7 @@
     kKeyMIMEType          = 'mime',  // cstring
     kKeyWidth             = 'widt',  // int32_t
     kKeyHeight            = 'heig',  // int32_t
+    kKeyRotation          = 'rotA',  // int32_t (angle in degrees)
     kKeyIFramesInterval   = 'ifiv',  // int32_t
     kKeyStride            = 'strd',  // int32_t
     kKeySliceHeight       = 'slht',  // int32_t
diff --git a/media/libmedia/IOMX.cpp b/media/libmedia/IOMX.cpp
index f3804b8..ae6c2bf 100644
--- a/media/libmedia/IOMX.cpp
+++ b/media/libmedia/IOMX.cpp
@@ -38,11 +38,13 @@
         const char *componentName,
         OMX_COLOR_FORMATTYPE colorFormat,
         size_t encodedWidth, size_t encodedHeight,
-        size_t displayWidth, size_t displayHeight) {
+        size_t displayWidth, size_t displayHeight,
+        int32_t rotationDegrees) {
     return createRenderer(
             surface->getISurface(),
             componentName, colorFormat, encodedWidth, encodedHeight,
-            displayWidth, displayHeight);
+            displayWidth, displayHeight,
+            rotationDegrees);
 }
 
 sp<IOMXRenderer> IOMX::createRendererFromJavaSurface(
@@ -50,7 +52,8 @@
         const char *componentName,
         OMX_COLOR_FORMATTYPE colorFormat,
         size_t encodedWidth, size_t encodedHeight,
-        size_t displayWidth, size_t displayHeight) {
+        size_t displayWidth, size_t displayHeight,
+        int32_t rotationDegrees) {
     jclass surfaceClass = env->FindClass("android/view/Surface");
     if (surfaceClass == NULL) {
         LOGE("Can't find android/view/Surface");
@@ -67,7 +70,8 @@
 
     return createRenderer(
             surface, componentName, colorFormat, encodedWidth,
-            encodedHeight, displayWidth, displayHeight);
+            encodedHeight, displayWidth, displayHeight,
+            rotationDegrees);
 }
 
 class BpOMX : public BpInterface<IOMX> {
@@ -349,7 +353,8 @@
             const char *componentName,
             OMX_COLOR_FORMATTYPE colorFormat,
             size_t encodedWidth, size_t encodedHeight,
-            size_t displayWidth, size_t displayHeight) {
+            size_t displayWidth, size_t displayHeight,
+            int32_t rotationDegrees) {
         Parcel data, reply;
         data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
 
@@ -360,6 +365,7 @@
         data.writeInt32(encodedHeight);
         data.writeInt32(displayWidth);
         data.writeInt32(displayHeight);
+        data.writeInt32(rotationDegrees);
 
         remote()->transact(CREATE_RENDERER, data, &reply);
 
@@ -682,11 +688,13 @@
             size_t encodedHeight = (size_t)data.readInt32();
             size_t displayWidth = (size_t)data.readInt32();
             size_t displayHeight = (size_t)data.readInt32();
+            int32_t rotationDegrees = data.readInt32();
 
             sp<IOMXRenderer> renderer =
                 createRenderer(isurface, componentName, colorFormat,
                                encodedWidth, encodedHeight,
-                               displayWidth, displayHeight);
+                               displayWidth, displayHeight,
+                               rotationDegrees);
 
             reply->writeStrongBinder(renderer->asBinder());
 
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index 064a00c..66eb7ee 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -103,12 +103,14 @@
             OMX_COLOR_FORMATTYPE colorFormat,
             const sp<ISurface> &surface,
             size_t displayWidth, size_t displayHeight,
-            size_t decodedWidth, size_t decodedHeight)
+            size_t decodedWidth, size_t decodedHeight,
+            int32_t rotationDegrees)
         : mTarget(NULL),
           mLibHandle(NULL) {
             init(previewOnly, componentName,
                  colorFormat, surface, displayWidth,
-                 displayHeight, decodedWidth, decodedHeight);
+                 displayHeight, decodedWidth, decodedHeight,
+                 rotationDegrees);
     }
 
     virtual void render(MediaBuffer *buffer) {
@@ -141,7 +143,8 @@
             OMX_COLOR_FORMATTYPE colorFormat,
             const sp<ISurface> &surface,
             size_t displayWidth, size_t displayHeight,
-            size_t decodedWidth, size_t decodedHeight);
+            size_t decodedWidth, size_t decodedHeight,
+            int32_t rotationDegrees);
 
     AwesomeLocalRenderer(const AwesomeLocalRenderer &);
     AwesomeLocalRenderer &operator=(const AwesomeLocalRenderer &);;
@@ -153,7 +156,8 @@
         OMX_COLOR_FORMATTYPE colorFormat,
         const sp<ISurface> &surface,
         size_t displayWidth, size_t displayHeight,
-        size_t decodedWidth, size_t decodedHeight) {
+        size_t decodedWidth, size_t decodedHeight,
+        int32_t rotationDegrees) {
     if (!previewOnly) {
         // We will stick to the vanilla software-color-converting renderer
         // for "previewOnly" mode, to avoid unneccessarily switching overlays
@@ -162,6 +166,14 @@
         mLibHandle = dlopen("libstagefrighthw.so", RTLD_NOW);
 
         if (mLibHandle) {
+            typedef VideoRenderer *(*CreateRendererWithRotationFunc)(
+                    const sp<ISurface> &surface,
+                    const char *componentName,
+                    OMX_COLOR_FORMATTYPE colorFormat,
+                    size_t displayWidth, size_t displayHeight,
+                    size_t decodedWidth, size_t decodedHeight,
+                    int32_t rotationDegrees);
+
             typedef VideoRenderer *(*CreateRendererFunc)(
                     const sp<ISurface> &surface,
                     const char *componentName,
@@ -169,17 +181,36 @@
                     size_t displayWidth, size_t displayHeight,
                     size_t decodedWidth, size_t decodedHeight);
 
-            CreateRendererFunc func =
-                (CreateRendererFunc)dlsym(
+            CreateRendererWithRotationFunc funcWithRotation =
+                (CreateRendererWithRotationFunc)dlsym(
                         mLibHandle,
-                        "_Z14createRendererRKN7android2spINS_8ISurfaceEEEPKc20"
-                        "OMX_COLOR_FORMATTYPEjjjj");
+                        "_Z26createRendererWithRotationRKN7android2spINS_8"
+                        "ISurfaceEEEPKc20OMX_COLOR_FORMATTYPEjjjji");
 
-            if (func) {
+            if (funcWithRotation) {
                 mTarget =
-                    (*func)(surface, componentName, colorFormat,
-                        displayWidth, displayHeight,
-                        decodedWidth, decodedHeight);
+                    (*funcWithRotation)(
+                            surface, componentName, colorFormat,
+                            displayWidth, displayHeight,
+                            decodedWidth, decodedHeight,
+                            rotationDegrees);
+            } else {
+                if (rotationDegrees != 0) {
+                    LOGW("renderer does not support rotation.");
+                }
+
+                CreateRendererFunc func =
+                    (CreateRendererFunc)dlsym(
+                            mLibHandle,
+                            "_Z14createRendererRKN7android2spINS_8ISurfaceEEEPKc20"
+                            "OMX_COLOR_FORMATTYPEjjjj");
+
+                if (func) {
+                    mTarget =
+                        (*func)(surface, componentName, colorFormat,
+                            displayWidth, displayHeight,
+                            decodedWidth, decodedHeight);
+                }
             }
         }
     }
@@ -187,7 +218,7 @@
     if (mTarget == NULL) {
         mTarget = new SoftwareRenderer(
                 colorFormat, surface, displayWidth, displayHeight,
-                decodedWidth, decodedHeight);
+                decodedWidth, decodedHeight, rotationDegrees);
     }
 }
 
@@ -785,6 +816,12 @@
         CHECK(meta->findInt32(kKeyWidth, &decodedWidth));
         CHECK(meta->findInt32(kKeyHeight, &decodedHeight));
 
+        int32_t rotationDegrees;
+        if (!mVideoTrack->getFormat()->findInt32(
+                    kKeyRotation, &rotationDegrees)) {
+            rotationDegrees = 0;
+        }
+
         mVideoRenderer.clear();
 
         // Must ensure that mVideoRenderer's destructor is actually executed
@@ -800,7 +837,8 @@
                         mISurface, component,
                         (OMX_COLOR_FORMATTYPE)format,
                         decodedWidth, decodedHeight,
-                        mVideoWidth, mVideoHeight));
+                        mVideoWidth, mVideoHeight,
+                        rotationDegrees));
         } else {
             // Other decoders are instantiated locally and as a consequence
             // allocate their buffers in local address space.
@@ -810,7 +848,7 @@
                 (OMX_COLOR_FORMATTYPE)format,
                 mISurface,
                 mVideoWidth, mVideoHeight,
-                decodedWidth, decodedHeight);
+                decodedWidth, decodedHeight, rotationDegrees);
         }
     }
 }
@@ -1625,7 +1663,22 @@
         if (mVideoWidth < 0 || mVideoHeight < 0) {
             notifyListener_l(MEDIA_SET_VIDEO_SIZE, 0, 0);
         } else {
-            notifyListener_l(MEDIA_SET_VIDEO_SIZE, mVideoWidth, mVideoHeight);
+            int32_t rotationDegrees;
+            if (!mVideoTrack->getFormat()->findInt32(
+                        kKeyRotation, &rotationDegrees)) {
+                rotationDegrees = 0;
+            }
+
+#if 1
+            if (rotationDegrees == 90 || rotationDegrees == 270) {
+                notifyListener_l(
+                        MEDIA_SET_VIDEO_SIZE, mVideoHeight, mVideoWidth);
+            } else
+#endif
+            {
+                notifyListener_l(
+                        MEDIA_SET_VIDEO_SIZE, mVideoWidth, mVideoHeight);
+            }
         }
 
         notifyListener_l(MEDIA_PREPARED);
@@ -1757,7 +1810,8 @@
                     state->mVideoWidth,
                     state->mVideoHeight,
                     state->mDecodedWidth,
-                    state->mDecodedHeight);
+                    state->mDecodedHeight,
+                    0);
 
         mVideoRendererIsPreview = true;
 
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index f404708..2154f2f 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -27,11 +27,11 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/DataSource.h>
 #include "include/ESDS.h"
 #include <media/stagefright/MediaBuffer.h>
 #include <media/stagefright/MediaBufferGroup.h>
-#include <media/stagefright/MediaDebug.h>
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MediaSource.h>
 #include <media/stagefright/MetaData.h>
@@ -579,52 +579,9 @@
 
         case FOURCC('t', 'k', 'h', 'd'):
         {
-            if (chunk_data_size < 4) {
-                return ERROR_MALFORMED;
-            }
-
-            uint8_t version;
-            if (mDataSource->readAt(data_offset, &version, 1) < 1) {
-                return ERROR_IO;
-            }
-
-            uint64_t ctime, mtime, duration;
-            int32_t id;
-            uint32_t width, height;
-
-            if (version == 1) {
-                if (chunk_data_size != 36 + 60) {
-                    return ERROR_MALFORMED;
-                }
-
-                uint8_t buffer[36 + 60];
-                if (mDataSource->readAt(
-                            data_offset, buffer, sizeof(buffer)) < (ssize_t)sizeof(buffer)) {
-                    return ERROR_IO;
-                }
-
-                ctime = U64_AT(&buffer[4]);
-                mtime = U64_AT(&buffer[12]);
-                id = U32_AT(&buffer[20]);
-                duration = U64_AT(&buffer[28]);
-                width = U32_AT(&buffer[88]);
-                height = U32_AT(&buffer[92]);
-            } else if (version == 0) {
-                if (chunk_data_size != 24 + 60) {
-                    return ERROR_MALFORMED;
-                }
-
-                uint8_t buffer[24 + 60];
-                if (mDataSource->readAt(
-                            data_offset, buffer, sizeof(buffer)) < (ssize_t)sizeof(buffer)) {
-                    return ERROR_IO;
-                }
-                ctime = U32_AT(&buffer[4]);
-                mtime = U32_AT(&buffer[8]);
-                id = U32_AT(&buffer[12]);
-                duration = U32_AT(&buffer[20]);
-                width = U32_AT(&buffer[76]);
-                height = U32_AT(&buffer[80]);
+            status_t err;
+            if ((err = parseTrackHeader(data_offset, chunk_data_size)) != OK) {
+                return err;
             }
 
             *offset += chunk_size;
@@ -1073,6 +1030,89 @@
     return OK;
 }
 
+status_t MPEG4Extractor::parseTrackHeader(
+        off_t data_offset, off_t data_size) {
+    if (data_size < 4) {
+        return ERROR_MALFORMED;
+    }
+
+    uint8_t version;
+    if (mDataSource->readAt(data_offset, &version, 1) < 1) {
+        return ERROR_IO;
+    }
+
+    size_t dynSize = (version == 1) ? 36 : 24;
+
+    uint8_t buffer[36 + 60];
+
+    if (data_size != (off_t)dynSize + 60) {
+        return ERROR_MALFORMED;
+    }
+
+    if (mDataSource->readAt(
+                data_offset, buffer, data_size) < (ssize_t)data_size) {
+        return ERROR_IO;
+    }
+
+    uint64_t ctime, mtime, duration;
+    int32_t id;
+
+    if (version == 1) {
+        ctime = U64_AT(&buffer[4]);
+        mtime = U64_AT(&buffer[12]);
+        id = U32_AT(&buffer[20]);
+        duration = U64_AT(&buffer[28]);
+    } else if (version == 0) {
+        ctime = U32_AT(&buffer[4]);
+        mtime = U32_AT(&buffer[8]);
+        id = U32_AT(&buffer[12]);
+        duration = U32_AT(&buffer[20]);
+    }
+
+    size_t matrixOffset = dynSize + 16;
+    int32_t a00 = U32_AT(&buffer[matrixOffset]);
+    int32_t a01 = U32_AT(&buffer[matrixOffset + 4]);
+    int32_t dx = U32_AT(&buffer[matrixOffset + 8]);
+    int32_t a10 = U32_AT(&buffer[matrixOffset + 12]);
+    int32_t a11 = U32_AT(&buffer[matrixOffset + 16]);
+    int32_t dy = U32_AT(&buffer[matrixOffset + 20]);
+
+#if 0
+    LOGI("x' = %.2f * x + %.2f * y + %.2f",
+         a00 / 65536.0f, a01 / 65536.0f, dx / 65536.0f);
+    LOGI("y' = %.2f * x + %.2f * y + %.2f",
+         a10 / 65536.0f, a11 / 65536.0f, dy / 65536.0f);
+#endif
+
+    uint32_t rotationDegrees;
+
+    static const int32_t kFixedOne = 0x10000;
+    if (a00 == kFixedOne && a01 == 0 && a10 == 0 && a11 == kFixedOne) {
+        // Identity, no rotation
+        rotationDegrees = 0;
+    } else if (a00 == 0 && a01 == kFixedOne && a10 == -kFixedOne && a11 == 0) {
+        rotationDegrees = 90;
+    } else if (a00 == 0 && a01 == -kFixedOne && a10 == kFixedOne && a11 == 0) {
+        rotationDegrees = 270;
+    } else if (a00 == -kFixedOne && a01 == 0 && a10 == 0 && a11 == -kFixedOne) {
+        rotationDegrees = 180;
+    } else {
+        LOGW("We only support 0,90,180,270 degree rotation matrices");
+        rotationDegrees = 0;
+    }
+
+    if (rotationDegrees != 0) {
+        mLastTrack->meta->setInt32(kKeyRotation, rotationDegrees);
+    }
+
+#if 0
+    uint32_t width = U32_AT(&buffer[dynSize + 52]);
+    uint32_t height = U32_AT(&buffer[dynSize + 56]);
+#endif
+
+    return OK;
+}
+
 status_t MPEG4Extractor::parseMetaData(off_t offset, size_t size) {
     if (size < 4) {
         return ERROR_MALFORMED;
@@ -1386,7 +1426,7 @@
         const uint8_t *ptr = (const uint8_t *)data;
 
         CHECK(size >= 7);
-        CHECK_EQ(ptr[0], 1);  // configurationVersion == 1
+        CHECK_EQ((unsigned)ptr[0], 1u);  // configurationVersion == 1
 
         // The number of bytes used to encode the length of a NAL unit.
         mNALLengthSize = 1 + (ptr[4] & 3);
@@ -1534,7 +1574,7 @@
         }
 
         uint32_t sampleTime;
-        CHECK_EQ(OK, mSampleTable->getMetaDataForSample(
+        CHECK_EQ((status_t)OK, mSampleTable->getMetaDataForSample(
                     sampleIndex, NULL, NULL, &sampleTime));
 
         if (mode == ReadOptions::SEEK_CLOSEST) {
@@ -1581,7 +1621,7 @@
         err = mGroup->acquire_buffer(&mBuffer);
 
         if (err != OK) {
-            CHECK_EQ(mBuffer, NULL);
+            CHECK(mBuffer == NULL);
             return err;
         }
     }
diff --git a/media/libstagefright/colorconversion/SoftwareRenderer.cpp b/media/libstagefright/colorconversion/SoftwareRenderer.cpp
index a6dbf69..86ad85b 100644
--- a/media/libstagefright/colorconversion/SoftwareRenderer.cpp
+++ b/media/libstagefright/colorconversion/SoftwareRenderer.cpp
@@ -30,7 +30,8 @@
         OMX_COLOR_FORMATTYPE colorFormat,
         const sp<ISurface> &surface,
         size_t displayWidth, size_t displayHeight,
-        size_t decodedWidth, size_t decodedHeight)
+        size_t decodedWidth, size_t decodedHeight,
+        int32_t rotationDegrees)
     : mColorFormat(colorFormat),
       mConverter(colorFormat, OMX_COLOR_Format16bitRGB565),
       mISurface(surface),
@@ -56,10 +57,20 @@
     CHECK(mMemoryHeap->heapID() >= 0);
     CHECK(mConverter.isValid());
 
+    uint32_t orientation;
+    switch (rotationDegrees) {
+        case 0: orientation = ISurface::BufferHeap::ROT_0; break;
+        case 90: orientation = ISurface::BufferHeap::ROT_90; break;
+        case 180: orientation = ISurface::BufferHeap::ROT_180; break;
+        case 270: orientation = ISurface::BufferHeap::ROT_270; break;
+        default: orientation = ISurface::BufferHeap::ROT_0; break;
+    }
+
     ISurface::BufferHeap bufferHeap(
             mDisplayWidth, mDisplayHeight,
             mDecodedWidth, mDecodedHeight,
             PIXEL_FORMAT_RGB_565,
+            orientation, 0,
             mMemoryHeap);
 
     status_t err = mISurface->registerBuffers(bufferHeap);
diff --git a/media/libstagefright/include/MPEG4Extractor.h b/media/libstagefright/include/MPEG4Extractor.h
index 1c9cc7e..2610b0e 100644
--- a/media/libstagefright/include/MPEG4Extractor.h
+++ b/media/libstagefright/include/MPEG4Extractor.h
@@ -71,6 +71,8 @@
 
     static status_t verifyTrack(Track *track);
 
+    status_t parseTrackHeader(off_t data_offset, off_t data_size);
+
     MPEG4Extractor(const MPEG4Extractor &);
     MPEG4Extractor &operator=(const MPEG4Extractor &);
 };
diff --git a/media/libstagefright/include/OMX.h b/media/libstagefright/include/OMX.h
index c99da59..72ab5aa 100644
--- a/media/libstagefright/include/OMX.h
+++ b/media/libstagefright/include/OMX.h
@@ -92,7 +92,8 @@
             const char *componentName,
             OMX_COLOR_FORMATTYPE colorFormat,
             size_t encodedWidth, size_t encodedHeight,
-            size_t displayWidth, size_t displayHeight);
+            size_t displayWidth, size_t displayHeight,
+            int32_t rotationDegrees);
 
     virtual void binderDied(const wp<IBinder> &the_late_who);
 
diff --git a/media/libstagefright/include/SoftwareRenderer.h b/media/libstagefright/include/SoftwareRenderer.h
index 9eed089..25c9df7 100644
--- a/media/libstagefright/include/SoftwareRenderer.h
+++ b/media/libstagefright/include/SoftwareRenderer.h
@@ -33,7 +33,8 @@
             OMX_COLOR_FORMATTYPE colorFormat,
             const sp<ISurface> &surface,
             size_t displayWidth, size_t displayHeight,
-            size_t decodedWidth, size_t decodedHeight);
+            size_t decodedWidth, size_t decodedHeight,
+            int32_t rotationDegrees = 0);
 
     virtual ~SoftwareRenderer();
 
diff --git a/media/libstagefright/omx/OMX.cpp b/media/libstagefright/omx/OMX.cpp
index c927da1..63af26a 100644
--- a/media/libstagefright/omx/OMX.cpp
+++ b/media/libstagefright/omx/OMX.cpp
@@ -459,7 +459,8 @@
         const char *componentName,
         OMX_COLOR_FORMATTYPE colorFormat,
         size_t encodedWidth, size_t encodedHeight,
-        size_t displayWidth, size_t displayHeight) {
+        size_t displayWidth, size_t displayHeight,
+        int32_t rotationDegrees) {
     Mutex::Autolock autoLock(mLock);
 
     VideoRenderer *impl = NULL;
@@ -467,6 +468,14 @@
     void *libHandle = dlopen("libstagefrighthw.so", RTLD_NOW);
 
     if (libHandle) {
+        typedef VideoRenderer *(*CreateRendererWithRotationFunc)(
+                const sp<ISurface> &surface,
+                const char *componentName,
+                OMX_COLOR_FORMATTYPE colorFormat,
+                size_t displayWidth, size_t displayHeight,
+                size_t decodedWidth, size_t decodedHeight,
+                int32_t rotationDegrees);
+
         typedef VideoRenderer *(*CreateRendererFunc)(
                 const sp<ISurface> &surface,
                 const char *componentName,
@@ -474,22 +483,35 @@
                 size_t displayWidth, size_t displayHeight,
                 size_t decodedWidth, size_t decodedHeight);
 
-        CreateRendererFunc func =
-            (CreateRendererFunc)dlsym(
+        CreateRendererWithRotationFunc funcWithRotation =
+            (CreateRendererWithRotationFunc)dlsym(
                     libHandle,
-                    "_Z14createRendererRKN7android2spINS_8ISurfaceEEEPKc20"
-                    "OMX_COLOR_FORMATTYPEjjjj");
+                    "_Z26createRendererWithRotationRKN7android2spINS_8"
+                    "ISurfaceEEEPKc20OMX_COLOR_FORMATTYPEjjjji");
 
-        if (func) {
-            impl = (*func)(surface, componentName, colorFormat,
-                    displayWidth, displayHeight, encodedWidth, encodedHeight);
+        if (funcWithRotation) {
+            impl = (*funcWithRotation)(
+                    surface, componentName, colorFormat,
+                    displayWidth, displayHeight, encodedWidth, encodedHeight,
+                    rotationDegrees);
+        } else {
+            CreateRendererFunc func =
+                (CreateRendererFunc)dlsym(
+                        libHandle,
+                        "_Z14createRendererRKN7android2spINS_8ISurfaceEEEPKc20"
+                        "OMX_COLOR_FORMATTYPEjjjj");
 
-            if (impl) {
-                impl = new SharedVideoRenderer(libHandle, impl);
-                libHandle = NULL;
+            if (func) {
+                impl = (*func)(surface, componentName, colorFormat,
+                        displayWidth, displayHeight, encodedWidth, encodedHeight);
             }
         }
 
+        if (impl) {
+            impl = new SharedVideoRenderer(libHandle, impl);
+            libHandle = NULL;
+        }
+
         if (libHandle) {
             dlclose(libHandle);
             libHandle = NULL;