Fix for 5156702 Rotate video output for thumbnails and export

This is part of a multi project submit.
This is the native engine part.

Change-Id: Icde922194a123a90544942caa12430fc3c4ef10e
diff --git a/libvideoeditor/lvpp/VideoEditorPreviewController.cpp b/libvideoeditor/lvpp/VideoEditorPreviewController.cpp
index 29dd150..27c0a0b 100755
--- a/libvideoeditor/lvpp/VideoEditorPreviewController.cpp
+++ b/libvideoeditor/lvpp/VideoEditorPreviewController.cpp
@@ -825,6 +825,26 @@
 
     pixelArray = NULL;
 
+    // Apply rotation if required
+    if (pFrameStr->videoRotationDegree != 0) {
+        err = applyVideoRotation((M4OSA_Void *)pFrameStr->pBuffer,
+                  pFrameStr->uiFrameWidth, pFrameStr->uiFrameHeight,
+                  pFrameStr->videoRotationDegree);
+        if (M4NO_ERROR != err) {
+            LOGE("renderPreviewFrame: cannot rotate video, err 0x%x", (unsigned int)err);
+            delete mTarget;
+            mTarget = NULL;
+            return err;
+        } else {
+           // Video rotation done.
+           // Swap width and height if 90 or 270 degrees
+           if (pFrameStr->videoRotationDegree != 180) {
+               int32_t temp = pFrameStr->uiFrameWidth;
+               pFrameStr->uiFrameWidth = pFrameStr->uiFrameHeight;
+               pFrameStr->uiFrameHeight = temp;
+           }
+        }
+    }
     // Postprocessing (apply video effect)
     if(pFrameStr->bApplyEffect == M4OSA_TRUE) {
 
diff --git a/libvideoeditor/lvpp/VideoEditorTools.cpp b/libvideoeditor/lvpp/VideoEditorTools.cpp
index c7c3650..f1a6c58 100755
--- a/libvideoeditor/lvpp/VideoEditorTools.cpp
+++ b/libvideoeditor/lvpp/VideoEditorTools.cpp
@@ -3672,3 +3672,214 @@
 
     return android::OK;
 }
+
+M4VIFI_UInt8 M4VIFI_Rotate90LeftYUV420toYUV420(void* pUserData,
+    M4VIFI_ImagePlane *pPlaneIn, M4VIFI_ImagePlane *pPlaneOut) {
+
+    M4VIFI_Int32 plane_number;
+    M4VIFI_UInt32 i,j, u_stride;
+    M4VIFI_UInt8 *p_buf_src, *p_buf_dest;
+
+    /**< Loop on Y,U and V planes */
+    for (plane_number = 0; plane_number < 3; plane_number++) {
+        /**< Get adresses of first valid pixel in input and output buffer */
+        /**< As we have a -90° rotation, first needed pixel is the upper-right one */
+        p_buf_src =
+            &(pPlaneIn[plane_number].pac_data[pPlaneIn[plane_number].u_topleft]) +
+             pPlaneOut[plane_number].u_height - 1 ;
+        p_buf_dest =
+            &(pPlaneOut[plane_number].pac_data[pPlaneOut[plane_number].u_topleft]);
+        u_stride = pPlaneIn[plane_number].u_stride;
+        /**< Loop on output rows */
+        for (i = 0; i < pPlaneOut[plane_number].u_height; i++) {
+            /**< Loop on all output pixels in a row */
+            for (j = 0; j < pPlaneOut[plane_number].u_width; j++) {
+                *p_buf_dest++= *p_buf_src;
+                p_buf_src += u_stride;  /**< Go to the next row */
+            }
+
+            /**< Go on next row of the output frame */
+            p_buf_dest +=
+                pPlaneOut[plane_number].u_stride - pPlaneOut[plane_number].u_width;
+            /**< Go to next pixel in the last row of the input frame*/
+            p_buf_src -=
+                pPlaneIn[plane_number].u_stride * pPlaneOut[plane_number].u_width + 1 ;
+        }
+    }
+
+    return M4VIFI_OK;
+}
+
+M4VIFI_UInt8 M4VIFI_Rotate90RightYUV420toYUV420(void* pUserData,
+    M4VIFI_ImagePlane *pPlaneIn, M4VIFI_ImagePlane *pPlaneOut) {
+
+    M4VIFI_Int32 plane_number;
+    M4VIFI_UInt32 i,j, u_stride;
+    M4VIFI_UInt8 *p_buf_src, *p_buf_dest;
+
+    /**< Loop on Y,U and V planes */
+    for (plane_number = 0; plane_number < 3; plane_number++) {
+        /**< Get adresses of first valid pixel in input and output buffer */
+        /**< As we have a +90° rotation, first needed pixel is the left-down one */
+        p_buf_src =
+            &(pPlaneIn[plane_number].pac_data[pPlaneIn[plane_number].u_topleft]) +
+             (pPlaneIn[plane_number].u_stride * (pPlaneOut[plane_number].u_width - 1));
+        p_buf_dest =
+            &(pPlaneOut[plane_number].pac_data[pPlaneOut[plane_number].u_topleft]);
+        u_stride = pPlaneIn[plane_number].u_stride;
+        /**< Loop on output rows */
+        for (i = 0; i < pPlaneOut[plane_number].u_height; i++) {
+            /**< Loop on all output pixels in a row */
+            for (j = 0; j < pPlaneOut[plane_number].u_width; j++) {
+                *p_buf_dest++= *p_buf_src;
+                p_buf_src -= u_stride;  /**< Go to the previous row */
+            }
+
+            /**< Go on next row of the output frame */
+            p_buf_dest +=
+                pPlaneOut[plane_number].u_stride - pPlaneOut[plane_number].u_width;
+            /**< Go to next pixel in the last row of the input frame*/
+            p_buf_src +=
+                pPlaneIn[plane_number].u_stride * pPlaneOut[plane_number].u_width +1 ;
+        }
+    }
+
+    return M4VIFI_OK;
+}
+
+M4VIFI_UInt8 M4VIFI_Rotate180YUV420toYUV420(void* pUserData,
+    M4VIFI_ImagePlane *pPlaneIn, M4VIFI_ImagePlane *pPlaneOut) {
+    M4VIFI_Int32 plane_number;
+    M4VIFI_UInt32 i,j;
+    M4VIFI_UInt8 *p_buf_src, *p_buf_dest, temp_pix1;
+
+    /**< Loop on Y,U and V planes */
+    for (plane_number = 0; plane_number < 3; plane_number++) {
+        /**< Get adresses of first valid pixel in input and output buffer */
+        p_buf_src =
+            &(pPlaneIn[plane_number].pac_data[pPlaneIn[plane_number].u_topleft]);
+        p_buf_dest =
+            &(pPlaneOut[plane_number].pac_data[pPlaneOut[plane_number].u_topleft]);
+
+        /**< If pPlaneIn = pPlaneOut, the algorithm will be different */
+        if (p_buf_src == p_buf_dest) {
+            /**< Get Address of last pixel in the last row of the frame */
+            p_buf_dest +=
+                pPlaneOut[plane_number].u_stride*(pPlaneOut[plane_number].u_height-1) +
+                 pPlaneOut[plane_number].u_width - 1;
+
+            /**< We loop (height/2) times on the rows.
+             * In case u_height is odd, the row at the middle of the frame
+             * has to be processed as must be mirrored */
+            for (i = 0; i < ((pPlaneOut[plane_number].u_height)>>1); i++) {
+                for (j = 0; j < pPlaneOut[plane_number].u_width; j++) {
+                    temp_pix1= *p_buf_dest;
+                    *p_buf_dest--= *p_buf_src;
+                    *p_buf_src++ = temp_pix1;
+                }
+                /**< Go on next row in top of frame */
+                p_buf_src +=
+                    pPlaneOut[plane_number].u_stride - pPlaneOut[plane_number].u_width;
+                /**< Go to the last pixel in previous row in bottom of frame*/
+                p_buf_dest -=
+                    pPlaneOut[plane_number].u_stride - pPlaneOut[plane_number].u_width;
+            }
+
+            /**< Mirror middle row in case height is odd */
+            if ((pPlaneOut[plane_number].u_height%2)!= 0) {
+                p_buf_src =
+                    &(pPlaneOut[plane_number].pac_data[pPlaneIn[plane_number].u_topleft]);
+                p_buf_src +=
+                    pPlaneOut[plane_number].u_stride*(pPlaneOut[plane_number].u_height>>1);
+                p_buf_dest =
+                    p_buf_src + pPlaneOut[plane_number].u_width;
+
+                /**< We loop u_width/2 times on this row.
+                 *  In case u_width is odd, the pixel at the middle of this row
+                 * remains unchanged */
+                for (j = 0; j < (pPlaneOut[plane_number].u_width>>1); j++) {
+                    temp_pix1= *p_buf_dest;
+                    *p_buf_dest--= *p_buf_src;
+                    *p_buf_src++ = temp_pix1;
+                }
+            }
+        } else {
+            /**< Get Address of last pixel in the last row of the output frame */
+            p_buf_dest +=
+                pPlaneOut[plane_number].u_stride*(pPlaneOut[plane_number].u_height-1) +
+                 pPlaneIn[plane_number].u_width - 1;
+
+            /**< Loop on rows */
+            for (i = 0; i < pPlaneOut[plane_number].u_height; i++) {
+                for (j = 0; j < pPlaneOut[plane_number].u_width; j++) {
+                    *p_buf_dest--= *p_buf_src++;
+                }
+
+                /**< Go on next row in top of input frame */
+                p_buf_src +=
+                    pPlaneIn[plane_number].u_stride - pPlaneOut[plane_number].u_width;
+                /**< Go to last pixel of previous row in bottom of input frame*/
+                p_buf_dest -=
+                    pPlaneOut[plane_number].u_stride - pPlaneOut[plane_number].u_width;
+            }
+        }
+    }
+
+    return M4VIFI_OK;
+}
+
+M4OSA_ERR applyVideoRotation(M4OSA_Void* pBuffer, M4OSA_UInt32 width,
+                             M4OSA_UInt32 height, M4OSA_UInt32 rotation) {
+
+    M4OSA_ERR err = M4NO_ERROR;
+    M4VIFI_ImagePlane planeIn[3], planeOut[3];
+
+    if (pBuffer == M4OSA_NULL) {
+        LOGE("applyVideoRotation: NULL input frame");
+        return M4ERR_PARAMETER;
+    }
+    M4OSA_UInt8* outPtr = (M4OSA_UInt8 *)M4OSA_32bitAlignedMalloc(
+     (width*height*1.5), M4VS, (M4OSA_Char*)("rotation out ptr"));
+    if (outPtr == M4OSA_NULL) {
+        return M4ERR_ALLOC;
+    }
+
+    // In plane
+    prepareYUV420ImagePlane(planeIn, width,
+        height, (M4VIFI_UInt8 *)pBuffer, width, height);
+
+    // Out plane
+    if (rotation != 180) {
+        prepareYUV420ImagePlane(planeOut, height,
+            width, outPtr, height, width);
+    }
+
+    switch(rotation) {
+        case 90:
+            M4VIFI_Rotate90RightYUV420toYUV420(M4OSA_NULL, planeIn, planeOut);
+            memset(pBuffer, 0, (width*height*1.5));
+            memcpy(pBuffer, (void *)outPtr, (width*height*1.5));
+            break;
+
+        case 180:
+            // In plane rotation, so planeOut = planeIn
+            M4VIFI_Rotate180YUV420toYUV420(M4OSA_NULL, planeIn, planeIn);
+            break;
+
+        case 270:
+            M4VIFI_Rotate90LeftYUV420toYUV420(M4OSA_NULL, planeIn, planeOut);
+            memset(pBuffer, 0, (width*height*1.5));
+            memcpy(pBuffer, (void *)outPtr, (width*height*1.5));
+            break;
+
+        default:
+            LOGE("invalid rotation param %d", (int)rotation);
+            err = M4ERR_PARAMETER;
+            break;
+    }
+
+    free((void *)outPtr);
+    return err;
+
+}
+
diff --git a/libvideoeditor/lvpp/VideoEditorTools.h b/libvideoeditor/lvpp/VideoEditorTools.h
index f39ed90..9b464da 100755
--- a/libvideoeditor/lvpp/VideoEditorTools.h
+++ b/libvideoeditor/lvpp/VideoEditorTools.h
@@ -139,4 +139,15 @@
 android::status_t getVideoSizeByResolution(M4VIDEOEDITING_VideoFrameSize resolution,
     uint32_t *pWidth, uint32_t *pHeight);
 
+M4VIFI_UInt8 M4VIFI_Rotate90LeftYUV420toYUV420(void* pUserData,
+    M4VIFI_ImagePlane *pPlaneIn, M4VIFI_ImagePlane *pPlaneOut);
+
+M4VIFI_UInt8 M4VIFI_Rotate90RightYUV420toYUV420(void* pUserData,
+    M4VIFI_ImagePlane *pPlaneIn, M4VIFI_ImagePlane *pPlaneOut);
+
+M4VIFI_UInt8 M4VIFI_Rotate180YUV420toYUV420(void* pUserData,
+    M4VIFI_ImagePlane *pPlaneIn, M4VIFI_ImagePlane *pPlaneOut);
+
+M4OSA_ERR applyVideoRotation(M4OSA_Void* pBuffer,
+    M4OSA_UInt32 width, M4OSA_UInt32 height, M4OSA_UInt32 rotation);
 #endif // ANDROID_VE_TOOLS_H
diff --git a/libvideoeditor/vss/common/inc/M4DA_Types.h b/libvideoeditor/vss/common/inc/M4DA_Types.h
index 42637e1..58cab7e 100755
--- a/libvideoeditor/vss/common/inc/M4DA_Types.h
+++ b/libvideoeditor/vss/common/inc/M4DA_Types.h
@@ -118,6 +118,7 @@
     M4OSA_UInt32        m_videoHeight;            /**< Height of the video in the stream */
     M4OSA_Float            m_averageFrameRate;        /**< Average frame rate of the video
                                                             in the stream */
+    M4OSA_Int32         videoRotationDegrees;        /**< Video rotation degree */
     M4OSA_UInt32        m_structSize;            /**< Size of the structure in bytes */
 } M4_VideoStreamHandler;
 
diff --git a/libvideoeditor/vss/common/inc/M4_VideoEditingCommon.h b/libvideoeditor/vss/common/inc/M4_VideoEditingCommon.h
index 5581417..9e7d03f 100755
--- a/libvideoeditor/vss/common/inc/M4_VideoEditingCommon.h
+++ b/libvideoeditor/vss/common/inc/M4_VideoEditingCommon.h
@@ -323,6 +323,8 @@
     M4OSA_UInt32                        uiClipAudioVolumePercentage;
     M4OSA_Bool                          bSetImageData;
 
+    M4OSA_Int32     videoRotationDegrees;        /**< Video rotation degree */
+
 } M4VIDEOEDITING_ClipProperties;
 
 
diff --git a/libvideoeditor/vss/inc/M4xVSS_Internal.h b/libvideoeditor/vss/inc/M4xVSS_Internal.h
index faf160f..5296572 100755
--- a/libvideoeditor/vss/inc/M4xVSS_Internal.h
+++ b/libvideoeditor/vss/inc/M4xVSS_Internal.h
@@ -485,7 +485,8 @@
 /**
  * Internal function prototypes */
 
-M4OSA_ERR M4xVSS_internalStartTranscoding(M4OSA_Context pContext);
+M4OSA_ERR M4xVSS_internalStartTranscoding(M4OSA_Context pContext,
+                                          M4OSA_UInt32 *rotationDegree);
 
 M4OSA_ERR M4xVSS_internalStopTranscoding(M4OSA_Context pContext);
 
diff --git a/libvideoeditor/vss/mcs/src/M4MCS_API.c b/libvideoeditor/vss/mcs/src/M4MCS_API.c
index baa0dd5..77990aa 100755
--- a/libvideoeditor/vss/mcs/src/M4MCS_API.c
+++ b/libvideoeditor/vss/mcs/src/M4MCS_API.c
@@ -9526,6 +9526,8 @@
             pC->pReaderVideoStream->m_averageFrameRate;
         pC->InputFileProperties.uiVideoMaxAuSize =
             pC->pReaderVideoStream->m_basicProperties.m_maxAUSize;
+        pC->InputFileProperties.videoRotationDegrees =
+            pC->pReaderVideoStream->videoRotationDegrees;
     }
     else
     {
diff --git a/libvideoeditor/vss/src/M4VD_EXTERNAL_BitstreamParser.c b/libvideoeditor/vss/src/M4VD_EXTERNAL_BitstreamParser.c
index 3026ab9..d1015d5 100755
--- a/libvideoeditor/vss/src/M4VD_EXTERNAL_BitstreamParser.c
+++ b/libvideoeditor/vss/src/M4VD_EXTERNAL_BitstreamParser.c
@@ -586,7 +586,7 @@
         default:
             *pLevel = M4VIDEOEDITING_VIDEO_UNKNOWN_LEVEL;
     }
-    LOGI("getAVCProfileAndLevel profile %ld level %ld", *pProfile, *pLevel);
+    LOGV("getAVCProfileAndLevel profile %ld level %ld", *pProfile, *pLevel);
     return M4NO_ERROR;
 }
 
@@ -670,7 +670,7 @@
         default:
            *pProfile = M4VIDEOEDITING_VIDEO_UNKNOWN_PROFILE;
     }
-    LOGI("getH263ProfileAndLevel profile %ld level %ld", *pProfile, *pLevel);
+    LOGV("getH263ProfileAndLevel profile %ld level %ld", *pProfile, *pLevel);
     return M4NO_ERROR;
 }
 
@@ -693,6 +693,6 @@
             break;
         }
     }
-    LOGI("getMPEG4ProfileAndLevel profile %ld level %ld", *pProfile, *pLevel);
+    LOGV("getMPEG4ProfileAndLevel profile %ld level %ld", *pProfile, *pLevel);
     return M4NO_ERROR;
 }
diff --git a/libvideoeditor/vss/src/M4VSS3GPP_EditVideo.c b/libvideoeditor/vss/src/M4VSS3GPP_EditVideo.c
index 3dd837a..3aafbe6 100755
--- a/libvideoeditor/vss/src/M4VSS3GPP_EditVideo.c
+++ b/libvideoeditor/vss/src/M4VSS3GPP_EditVideo.c
@@ -104,6 +104,12 @@
                                              M4VIFI_ImagePlane *pPlaneNoResize,
                                              M4VIFI_ImagePlane *pPlaneOut);
 
+static M4OSA_ERR M4VSS3GPP_intRotateVideo(M4VIFI_ImagePlane* pPlaneIn,
+                                      M4OSA_UInt32 rotationDegree);
+
+static M4OSA_ERR M4VSS3GPP_intSetYUV420Plane(M4VIFI_ImagePlane* planeIn,
+                                      M4OSA_UInt32 width, M4OSA_UInt32 height);
+
 /**
  ******************************************************************************
  * M4OSA_ERR M4VSS3GPP_intEditStepVideo()
@@ -1055,7 +1061,7 @@
     M4VIFI_ImagePlane *pDecoderRenderFrame = M4OSA_NULL;
     M4VIFI_ImagePlane pTemp1[3],pTemp2[3];
     M4VIFI_ImagePlane pTempPlaneClip1[3],pTempPlaneClip2[3];
-    M4OSA_UInt32  i = 0;
+    M4OSA_UInt32  i = 0, yuvFrameWidth = 0, yuvFrameHeight = 0;
 
     /**
     * VPP context is actually the VSS3GPP context */
@@ -1345,6 +1351,22 @@
                     pC->ewc.VppError = err;
                     return M4NO_ERROR;
                 }
+                if (pC->pC1->pSettings->FileType !=
+                        M4VIDEOEDITING_kFileType_ARGB8888) {
+                    if (0 != pC->pC1->pSettings->ClipProperties.videoRotationDegrees) {
+                        // Save width and height of un-rotated frame
+                        yuvFrameWidth = pC->pC1->m_pPreResizeFrame[0].u_width;
+                        yuvFrameHeight = pC->pC1->m_pPreResizeFrame[0].u_height;
+                        err = M4VSS3GPP_intRotateVideo(pC->pC1->m_pPreResizeFrame,
+                                pC->pC1->pSettings->ClipProperties.videoRotationDegrees);
+                        if (M4NO_ERROR != err) {
+                            M4OSA_TRACE1_1("M4VSS3GPP_intVPP: \
+                                rotateVideo() returns error 0x%x", err);
+                            pC->ewc.VppError = err;
+                            return M4NO_ERROR;
+                        }
+                    }
+                }
 
                 if (pC->nbActiveEffects > 0) {
                     pC->pC1->bGetYuvDataFromDecoder = M4OSA_TRUE;
@@ -1407,11 +1429,21 @@
                     }
                     pC->pC1->bGetYuvDataFromDecoder = M4OSA_FALSE;
                 }
+
+                // Reset original width and height for resize frame plane
+                if (0 != pC->pC1->pSettings->ClipProperties.videoRotationDegrees &&
+                    180 != pC->pC1->pSettings->ClipProperties.videoRotationDegrees) {
+
+                    M4VSS3GPP_intSetYUV420Plane(pC->pC1->m_pPreResizeFrame,
+                                                yuvFrameWidth, yuvFrameHeight);
+                }
             }
             else
             {
                 M4OSA_TRACE3_0("M4VSS3GPP_intVPP: NO resize required");
-                if (pC->nbActiveEffects > 0) {
+                if ((pC->nbActiveEffects > 0) ||
+                    ((0 != pC->pC1->pSettings->ClipProperties.videoRotationDegrees)
+                     && (180 != pC->pC1->pSettings->ClipProperties.videoRotationDegrees))) {
                     /** If we do modify the image, we need an
                      * intermediate image plane */
                     if (M4OSA_NULL == pTemp1[0].pac_data) {
@@ -1438,14 +1470,78 @@
                     return M4NO_ERROR;
                 }
 
+                if (pC->pC1->pSettings->FileType !=
+                        M4VIDEOEDITING_kFileType_ARGB8888) {
+                    if (0 != pC->pC1->pSettings->ClipProperties.videoRotationDegrees) {
+                        // Save width and height of un-rotated frame
+                        yuvFrameWidth = pDecoderRenderFrame[0].u_width;
+                        yuvFrameHeight = pDecoderRenderFrame[0].u_height;
+                        err = M4VSS3GPP_intRotateVideo(pDecoderRenderFrame,
+                            pC->pC1->pSettings->ClipProperties.videoRotationDegrees);
+                        if (M4NO_ERROR != err) {
+                            M4OSA_TRACE1_1("M4VSS3GPP_intVPP: \
+                                rotateVideo() returns error 0x%x", err);
+                            pC->ewc.VppError = err;
+                            return M4NO_ERROR;
+                        }
+
+                        if (180 != pC->pC1->pSettings->ClipProperties.videoRotationDegrees) {
+                            // Apply black border on rotated frame
+                            if (pC->nbActiveEffects > 0) {
+                                /** we need an intermediate image plane */
+                                if (M4OSA_NULL == pTemp2[0].pac_data) {
+                                    err = M4VSS3GPP_intAllocateYUV420(pTemp2,
+                                              pC->ewc.uiVideoWidth,
+                                              pC->ewc.uiVideoHeight);
+                                    if (M4NO_ERROR != err) {
+                                        pC->ewc.VppError = err;
+                                        return M4NO_ERROR;
+                                    }
+                                }
+                                err = M4VSS3GPP_intApplyRenderingMode(pC, M4xVSS_kBlackBorders,
+                                        pDecoderRenderFrame, pTemp2);
+                            } else {
+                                err = M4VSS3GPP_intApplyRenderingMode(pC, M4xVSS_kBlackBorders,
+                                        pDecoderRenderFrame, pTmp);
+                            }
+                            if (M4NO_ERROR != err) {
+                                M4OSA_TRACE1_1("M4VSS3GPP_intVPP: \
+                                    M4VSS3GPP_intApplyRenderingMode) error 0x%x ", err);
+                                pC->ewc.VppError = err;
+                                return M4NO_ERROR;
+                            }
+                        }
+                    }
+                }
+
                 if (pC->nbActiveEffects > 0) {
-                    err = M4VSS3GPP_intApplyVideoEffect(pC,
-                              pDecoderRenderFrame,pPlaneOut);
+                    if ((0 != pC->pC1->pSettings->ClipProperties.videoRotationDegrees) &&
+                        (180 != pC->pC1->pSettings->ClipProperties.videoRotationDegrees)) {
+                        err = M4VSS3GPP_intApplyVideoEffect(pC,
+                                  pTemp2,pPlaneOut);
+                    } else {
+                        err = M4VSS3GPP_intApplyVideoEffect(pC,
+                                  pDecoderRenderFrame,pPlaneOut);
+                    }
                     if (M4NO_ERROR != err) {
                         pC->ewc.VppError = err;
                         return M4NO_ERROR;
                     }
                 }
+
+                // Reset original width and height for resize frame plane
+                if (0 != pC->pC1->pSettings->ClipProperties.videoRotationDegrees &&
+                    180 != pC->pC1->pSettings->ClipProperties.videoRotationDegrees) {
+
+                    M4VSS3GPP_intSetYUV420Plane(pDecoderRenderFrame,
+                                                yuvFrameWidth, yuvFrameHeight);
+
+                    if (pC->nbActiveEffects > 0) {
+                        free((void *)pTemp2[0].pac_data);
+                        free((void *)pTemp2[1].pac_data);
+                        free((void *)pTemp2[2].pac_data);
+                    }
+                }
             }
             pC->pC1->lastDecodedPlane = pTmp;
             pC->pC1->iVideoRenderCts = (M4OSA_Int32)ts;
@@ -3297,6 +3393,9 @@
     M4OSA_ERR err = M4NO_ERROR;
     M4OSA_UInt8 numEffects = 0;
     M4VIFI_ImagePlane *pDecoderRenderFrame = M4OSA_NULL;
+    M4OSA_UInt32 yuvFrameWidth = 0, yuvFrameHeight = 0;
+    M4VIFI_ImagePlane* pTmp = M4OSA_NULL;
+    M4VIFI_ImagePlane pTemp[3];
 
     /**
     Check if resizing is needed */
@@ -3350,6 +3449,22 @@
             return err;
         }
 
+        if (pClipCtxt->pSettings->FileType !=
+                M4VIDEOEDITING_kFileType_ARGB8888) {
+            if (0 != pClipCtxt->pSettings->ClipProperties.videoRotationDegrees) {
+                // Save width and height of un-rotated frame
+                yuvFrameWidth = pClipCtxt->m_pPreResizeFrame[0].u_width;
+                yuvFrameHeight = pClipCtxt->m_pPreResizeFrame[0].u_height;
+                err = M4VSS3GPP_intRotateVideo(pClipCtxt->m_pPreResizeFrame,
+                    pClipCtxt->pSettings->ClipProperties.videoRotationDegrees);
+                if (M4NO_ERROR != err) {
+                    M4OSA_TRACE1_1("M4VSS3GPP_intRenderFrameWithEffect: \
+                        rotateVideo() returns error 0x%x", err);
+                    return err;
+                }
+            }
+        }
+
         if (bIsClip1 == M4OSA_TRUE) {
             numEffects = pC->nbActiveEffects;
         } else {
@@ -3423,6 +3538,14 @@
             pClipCtxt->bGetYuvDataFromDecoder = M4OSA_FALSE;
         }
 
+        // Reset original width and height for resize frame plane
+        if (0 != pClipCtxt->pSettings->ClipProperties.videoRotationDegrees &&
+            180 != pClipCtxt->pSettings->ClipProperties.videoRotationDegrees) {
+
+            M4VSS3GPP_intSetYUV420Plane(pClipCtxt->m_pPreResizeFrame,
+                                        yuvFrameWidth, yuvFrameHeight);
+        }
+
     } else {
         if (bIsClip1 == M4OSA_TRUE) {
             numEffects = pC->nbActiveEffects;
@@ -3439,13 +3562,64 @@
                     Render returns error 0x%x", err);
                 return err;
             }
+
+            if (pClipCtxt->pSettings->FileType !=
+                    M4VIDEOEDITING_kFileType_ARGB8888) {
+                if (0 != pClipCtxt->pSettings->ClipProperties.videoRotationDegrees) {
+                    // Save width and height of un-rotated frame
+                    yuvFrameWidth = pPlaneNoResize[0].u_width;
+                    yuvFrameHeight = pPlaneNoResize[0].u_height;
+                    err = M4VSS3GPP_intRotateVideo(pPlaneNoResize,
+                        pClipCtxt->pSettings->ClipProperties.videoRotationDegrees);
+                    if (M4NO_ERROR != err) {
+                        M4OSA_TRACE1_1("M4VSS3GPP_intRenderFrameWithEffect: \
+                            rotateVideo() returns error 0x%x", err);
+                        return err;
+                    }
+                }
+
+                if (180 != pClipCtxt->pSettings->ClipProperties.videoRotationDegrees) {
+                    // Apply Black Borders to rotated plane
+                    /** we need an intermediate image plane */
+
+                    err = M4VSS3GPP_intAllocateYUV420(pTemp,
+                              pC->ewc.uiVideoWidth,
+                              pC->ewc.uiVideoHeight);
+                    if (M4NO_ERROR != err) {
+                        M4OSA_TRACE1_1("M4VSS3GPP_intRenderFrameWithEffect: \
+                            memAlloc() returns error 0x%x", err);
+                        return err;
+                    }
+                    err = M4VSS3GPP_intApplyRenderingMode(pC, M4xVSS_kBlackBorders,
+                              pPlaneNoResize, pTemp);
+                    if (M4NO_ERROR != err) {
+                        M4OSA_TRACE1_1("M4VSS3GPP_intRenderFrameWithEffect: \
+                            M4VSS3GPP_intApplyRenderingMode() returns error 0x%x", err);
+                        free((void *)pTemp[0].pac_data);
+                        free((void *)pTemp[1].pac_data);
+                        free((void *)pTemp[2].pac_data);
+                        return err;
+                    }
+                }
+            }
+
             if (bIsClip1 == M4OSA_TRUE) {
                 pC->bIssecondClip = M4OSA_FALSE;
-                err = M4VSS3GPP_intApplyVideoEffect(pC, pPlaneNoResize ,pC->yuv1);
+                if ((0 != pClipCtxt->pSettings->ClipProperties.videoRotationDegrees) &&
+                    (180 != pClipCtxt->pSettings->ClipProperties.videoRotationDegrees)) {
+                    err = M4VSS3GPP_intApplyVideoEffect(pC, pTemp ,pC->yuv1);
+                } else {
+                    err = M4VSS3GPP_intApplyVideoEffect(pC, pPlaneNoResize ,pC->yuv1);
+                }
                 pClipCtxt->lastDecodedPlane = pC->yuv1;
             } else {
                 pC->bIssecondClip = M4OSA_TRUE;
-                err = M4VSS3GPP_intApplyVideoEffect(pC, pPlaneNoResize ,pC->yuv2);
+                if ((0 != pClipCtxt->pSettings->ClipProperties.videoRotationDegrees) &&
+                    (180 != pClipCtxt->pSettings->ClipProperties.videoRotationDegrees)) {
+                    err = M4VSS3GPP_intApplyVideoEffect(pC, pTemp ,pC->yuv2);
+                } else {
+                    err = M4VSS3GPP_intApplyVideoEffect(pC, pPlaneNoResize ,pC->yuv2);
+                }
                 pClipCtxt->lastDecodedPlane = pC->yuv2;
             }
 
@@ -3455,24 +3629,212 @@
                 return err;
             }
 
-        } else {
-            if (bIsClip1 == M4OSA_TRUE) {
-                err = pClipCtxt->ShellAPI.m_pVideoDecoder->m_pFctRender(
-                          pClipCtxt->pViDecCtxt, &ts, pC->yuv1, M4OSA_TRUE);
-                pClipCtxt->lastDecodedPlane = pC->yuv1;
-            } else {
-                err = pClipCtxt->ShellAPI.m_pVideoDecoder->m_pFctRender(
-                      pClipCtxt->pViDecCtxt, &ts, pC->yuv2, M4OSA_TRUE);
-                pClipCtxt->lastDecodedPlane = pC->yuv2;
+            // Reset original width and height for resize frame plane
+            if (0 != pClipCtxt->pSettings->ClipProperties.videoRotationDegrees &&
+                180 != pClipCtxt->pSettings->ClipProperties.videoRotationDegrees) {
+
+                M4VSS3GPP_intSetYUV420Plane(pPlaneNoResize,
+                                            yuvFrameWidth, yuvFrameHeight);
+
+                free((void *)pTemp[0].pac_data);
+                free((void *)pTemp[1].pac_data);
+                free((void *)pTemp[2].pac_data);
             }
+
+        } else {
+
+            if ((0 != pClipCtxt->pSettings->ClipProperties.videoRotationDegrees) &&
+                (180 != pClipCtxt->pSettings->ClipProperties.videoRotationDegrees)) {
+                pTmp = pPlaneNoResize;
+            } else if (bIsClip1 == M4OSA_TRUE) {
+                pTmp = pC->yuv1;
+            } else {
+                pTmp = pC->yuv2;
+            }
+            err = pClipCtxt->ShellAPI.m_pVideoDecoder->m_pFctRender(
+                      pClipCtxt->pViDecCtxt, &ts, pTmp, M4OSA_TRUE);
             if (M4NO_ERROR != err) {
                 M4OSA_TRACE1_1("M4VSS3GPP_intRenderFrameWithEffect: \
                     Render returns error 0x%x,", err);
                 return err;
             }
+
+            if (0 == pClipCtxt->pSettings->ClipProperties.videoRotationDegrees) {
+                pClipCtxt->lastDecodedPlane = pTmp;
+            } else {
+                // Save width and height of un-rotated frame
+                yuvFrameWidth = pTmp[0].u_width;
+                yuvFrameHeight = pTmp[0].u_height;
+                err = M4VSS3GPP_intRotateVideo(pTmp,
+                    pClipCtxt->pSettings->ClipProperties.videoRotationDegrees);
+                if (M4NO_ERROR != err) {
+                    M4OSA_TRACE1_1("M4VSS3GPP_intRenderFrameWithEffect: \
+                        rotateVideo() returns error 0x%x", err);
+                    return err;
+                }
+
+                if (180 != pClipCtxt->pSettings->ClipProperties.videoRotationDegrees) {
+
+                    // Apply Black borders on rotated frame
+                    if (bIsClip1) {
+                        err = M4VSS3GPP_intApplyRenderingMode (pC,
+                            M4xVSS_kBlackBorders,
+                            pTmp,pC->yuv1);
+                    } else {
+                        err = M4VSS3GPP_intApplyRenderingMode (pC,
+                            M4xVSS_kBlackBorders,
+                            pTmp,pC->yuv2);
+                    }
+                    if (M4NO_ERROR != err) {
+                        M4OSA_TRACE1_1("M4VSS3GPP_intRenderFrameWithEffect: \
+                            M4VSS3GPP_intApplyRenderingMode error 0x%x", err);
+                        return err;
+                    }
+
+                    // Reset original width and height for noresize frame plane
+                    M4VSS3GPP_intSetYUV420Plane(pPlaneNoResize,
+                                                yuvFrameWidth, yuvFrameHeight);
+                }
+
+                if (bIsClip1) {
+                    pClipCtxt->lastDecodedPlane = pC->yuv1;
+                } else {
+                    pClipCtxt->lastDecodedPlane = pC->yuv2;
+                }
+            }
         }
         pClipCtxt->iVideoRenderCts = (M4OSA_Int32)ts;
     }
 
     return err;
 }
+
+M4OSA_ERR M4VSS3GPP_intRotateVideo(M4VIFI_ImagePlane* pPlaneIn,
+                                   M4OSA_UInt32 rotationDegree) {
+
+    M4OSA_ERR err = M4NO_ERROR;
+    M4VIFI_ImagePlane outPlane[3];
+
+    if (rotationDegree != 180) {
+        // Swap width and height of in plane
+        outPlane[0].u_width = pPlaneIn[0].u_height;
+        outPlane[0].u_height = pPlaneIn[0].u_width;
+        outPlane[0].u_stride = outPlane[0].u_width;
+        outPlane[0].u_topleft = 0;
+        outPlane[0].pac_data = (M4OSA_UInt8 *)M4OSA_32bitAlignedMalloc(
+            (outPlane[0].u_stride*outPlane[0].u_height), M4VS,
+            (M4OSA_Char*)("out Y plane for rotation"));
+        if (outPlane[0].pac_data == M4OSA_NULL) {
+            return M4ERR_ALLOC;
+        }
+
+        outPlane[1].u_width = pPlaneIn[0].u_height/2;
+        outPlane[1].u_height = pPlaneIn[0].u_width/2;
+        outPlane[1].u_stride = outPlane[1].u_width;
+        outPlane[1].u_topleft = 0;
+        outPlane[1].pac_data = (M4OSA_UInt8 *)M4OSA_32bitAlignedMalloc(
+            (outPlane[1].u_stride*outPlane[1].u_height), M4VS,
+            (M4OSA_Char*)("out U plane for rotation"));
+        if (outPlane[1].pac_data == M4OSA_NULL) {
+            free((void *)outPlane[0].pac_data);
+            return M4ERR_ALLOC;
+        }
+
+        outPlane[2].u_width = pPlaneIn[0].u_height/2;
+        outPlane[2].u_height = pPlaneIn[0].u_width/2;
+        outPlane[2].u_stride = outPlane[2].u_width;
+        outPlane[2].u_topleft = 0;
+        outPlane[2].pac_data = (M4OSA_UInt8 *)M4OSA_32bitAlignedMalloc(
+            (outPlane[2].u_stride*outPlane[2].u_height), M4VS,
+            (M4OSA_Char*)("out V plane for rotation"));
+        if (outPlane[2].pac_data == M4OSA_NULL) {
+            free((void *)outPlane[0].pac_data);
+            free((void *)outPlane[1].pac_data);
+            return M4ERR_ALLOC;
+        }
+    }
+
+    switch(rotationDegree) {
+        case 90:
+            M4VIFI_Rotate90RightYUV420toYUV420(M4OSA_NULL, pPlaneIn, outPlane);
+            break;
+
+        case 180:
+            // In plane rotation, so planeOut = planeIn
+            M4VIFI_Rotate180YUV420toYUV420(M4OSA_NULL, pPlaneIn, pPlaneIn);
+            break;
+
+        case 270:
+            M4VIFI_Rotate90LeftYUV420toYUV420(M4OSA_NULL, pPlaneIn, outPlane);
+            break;
+
+        default:
+            M4OSA_TRACE1_1("invalid rotation param %d", (int)rotationDegree);
+            err = M4ERR_PARAMETER;
+            break;
+    }
+
+    if (rotationDegree != 180) {
+        memset((void *)pPlaneIn[0].pac_data, 0,
+            (pPlaneIn[0].u_width*pPlaneIn[0].u_height));
+        memset((void *)pPlaneIn[1].pac_data, 0,
+            (pPlaneIn[1].u_width*pPlaneIn[1].u_height));
+        memset((void *)pPlaneIn[2].pac_data, 0,
+            (pPlaneIn[2].u_width*pPlaneIn[2].u_height));
+        // Copy Y, U and V planes
+        memcpy((void *)pPlaneIn[0].pac_data, (void *)outPlane[0].pac_data,
+            (pPlaneIn[0].u_width*pPlaneIn[0].u_height));
+        memcpy((void *)pPlaneIn[1].pac_data, (void *)outPlane[1].pac_data,
+            (pPlaneIn[1].u_width*pPlaneIn[1].u_height));
+        memcpy((void *)pPlaneIn[2].pac_data, (void *)outPlane[2].pac_data,
+            (pPlaneIn[2].u_width*pPlaneIn[2].u_height));
+
+        free((void *)outPlane[0].pac_data);
+        free((void *)outPlane[1].pac_data);
+        free((void *)outPlane[2].pac_data);
+
+        // Swap the width and height of the in plane
+        uint32_t temp = 0;
+        temp = pPlaneIn[0].u_width;
+        pPlaneIn[0].u_width = pPlaneIn[0].u_height;
+        pPlaneIn[0].u_height = temp;
+        pPlaneIn[0].u_stride = pPlaneIn[0].u_width;
+
+        temp = pPlaneIn[1].u_width;
+        pPlaneIn[1].u_width = pPlaneIn[1].u_height;
+        pPlaneIn[1].u_height = temp;
+        pPlaneIn[1].u_stride = pPlaneIn[1].u_width;
+
+        temp = pPlaneIn[2].u_width;
+        pPlaneIn[2].u_width = pPlaneIn[2].u_height;
+        pPlaneIn[2].u_height = temp;
+        pPlaneIn[2].u_stride = pPlaneIn[2].u_width;
+    }
+
+    return err;
+}
+
+M4OSA_ERR M4VSS3GPP_intSetYUV420Plane(M4VIFI_ImagePlane* planeIn,
+                                      M4OSA_UInt32 width, M4OSA_UInt32 height) {
+
+    M4OSA_ERR err = M4NO_ERROR;
+
+    if (planeIn == M4OSA_NULL) {
+        M4OSA_TRACE1_0("NULL in plane, error");
+        return M4ERR_PARAMETER;
+    }
+
+    planeIn[0].u_width = width;
+    planeIn[0].u_height = height;
+    planeIn[0].u_stride = planeIn[0].u_width;
+
+    planeIn[1].u_width = width/2;
+    planeIn[1].u_height = height/2;
+    planeIn[1].u_stride = planeIn[1].u_width;
+
+    planeIn[2].u_width = width/2;
+    planeIn[2].u_height = height/2;
+    planeIn[2].u_stride = planeIn[1].u_width;
+
+    return err;
+}
diff --git a/libvideoeditor/vss/src/M4xVSS_API.c b/libvideoeditor/vss/src/M4xVSS_API.c
index a654f88..2ff1a88 100755
--- a/libvideoeditor/vss/src/M4xVSS_API.c
+++ b/libvideoeditor/vss/src/M4xVSS_API.c
@@ -5789,24 +5789,23 @@
                     if( xVSS_context->pMCScurrentParams->isCreated == M4OSA_FALSE )
                     {
                         /* Opening MCS */
-                        err = M4xVSS_internalStartTranscoding(xVSS_context);
+                        M4OSA_UInt32 rotationDegree = 0;
+                        err = M4xVSS_internalStartTranscoding(xVSS_context, &rotationDegree);
 
                         if( err != M4NO_ERROR )
                         {
                             M4OSA_TRACE1_1("M4xVSS_Step: M4xVSS_internalStartTranscoding returned\
-                                 error: 0x%x", err)
-                                           /* TODO ? : Translate error code of MCS to an xVSS error
-                                           code ? */
-                                           return err;
+                                 error: 0x%x", err);
+                            return err;
                         }
-                    int32_t index = xVSS_context->pMCScurrentParams->videoclipnumber;
-                    if(xVSS_context->pSettings->pClipList[index]->bTranscodingRequired
-                     == M4OSA_FALSE) {
-                        /*the cuts are done in the MCS, so we need to replace
-                           the beginCutTime and endCutTime to keep the entire video*/
-                        xVSS_context->pSettings->pClipList[index]->uiBeginCutTime = 0;
-                        xVSS_context->pSettings->pClipList[index]->uiEndCutTime = 0;
-                    }
+                        int32_t index = xVSS_context->pMCScurrentParams->videoclipnumber;
+                        if(xVSS_context->pSettings->pClipList[index]->bTranscodingRequired
+                         == M4OSA_FALSE) {
+                            /*the cuts are done in the MCS, so we need to replace
+                               the beginCutTime and endCutTime to keep the entire video*/
+                            xVSS_context->pSettings->pClipList[index]->uiBeginCutTime = 0;
+                            xVSS_context->pSettings->pClipList[index]->uiEndCutTime = 0;
+                        }
 
                         M4OSA_TRACE1_1("M4xVSS_Step: \
                             M4xVSS_internalStartTranscoding returned \
@@ -5814,6 +5813,10 @@
                                  xVSS_context->pMCS_Ctxt);
                         xVSS_context->analyseStep =
                             M4xVSS_kMicroStateTranscodeMCS;
+
+                        // Retain rotation info of trimmed / transcoded file
+                        xVSS_context->pSettings->pClipList[index]->\
+                            ClipProperties.videoRotationDegrees = rotationDegree;
                     }
                 }
                 else if( xVSS_context->analyseStep
diff --git a/libvideoeditor/vss/src/M4xVSS_internal.c b/libvideoeditor/vss/src/M4xVSS_internal.c
index f4f4137..a322caa 100755
--- a/libvideoeditor/vss/src/M4xVSS_internal.c
+++ b/libvideoeditor/vss/src/M4xVSS_internal.c
@@ -76,7 +76,8 @@
  * @return    M4ERR_ALLOC:        Memory allocation has failed
  ******************************************************************************
  */
-M4OSA_ERR M4xVSS_internalStartTranscoding(M4OSA_Context pContext)
+M4OSA_ERR M4xVSS_internalStartTranscoding(M4OSA_Context pContext,
+                                          M4OSA_UInt32 *rotationDegree)
 {
     M4xVSS_Context* xVSS_context = (M4xVSS_Context*)pContext;
     M4OSA_ERR err;
@@ -84,6 +85,7 @@
     M4MCS_OutputParams Params;
     M4MCS_EncodingParams Rates;
     M4OSA_UInt32 i;
+    M4VIDEOEDITING_ClipProperties clipProps;
 
     err = M4MCS_init(&mcs_context, xVSS_context->pFileReadPtr, xVSS_context->pFileWritePtr);
     if(err != M4NO_ERROR)
@@ -103,6 +105,16 @@
         return err;
     }
 
+    /** Get the clip properties
+     */
+    err = M4MCS_getInputFileProperties(mcs_context, &clipProps);
+    if (err != M4NO_ERROR) {
+        M4OSA_TRACE1_1("Error in M4MCS_getInputFileProperties: 0x%x", err);
+        M4MCS_abort(mcs_context);
+        return err;
+    }
+    *rotationDegree = clipProps.videoRotationDegrees;
+
     /**
      * Fill MCS parameters with the parameters contained in the current element of the
        MCS parameters chained list */
diff --git a/libvideoeditor/vss/stagefrightshells/src/VideoEditor3gpReader.cpp b/libvideoeditor/vss/stagefrightshells/src/VideoEditor3gpReader.cpp
index b91c2ee..52478ee 100755
--- a/libvideoeditor/vss/stagefrightshells/src/VideoEditor3gpReader.cpp
+++ b/libvideoeditor/vss/stagefrightshells/src/VideoEditor3gpReader.cpp
@@ -1519,6 +1519,13 @@
                 LOGV("<<<<<<<<<<   video: Average FPS from MP4 extractor in FLOAT: %f",
                     pVideoStreamHandler->m_averageFrameRate);
 
+                // Get the video rotation degree
+                int32_t rotationDegree;
+                if(!meta->findInt32(kKeyRotation, &rotationDegree)) {
+                    rotationDegree = 0;
+                }
+                pVideoStreamHandler->videoRotationDegrees = rotationDegree;
+
                 pC->mVideoStreamHandler =
                     (M4_StreamHandler*)(pVideoStreamHandler);