void
MediaEngineGonkVideoSource::RotateImage(layers::Image* aImage, uint32_t aWidth, uint32_t aHeight) {
  layers::GrallocImage *nativeImage = static_cast<layers::GrallocImage*>(aImage);
  android::sp<GraphicBuffer> graphicBuffer = nativeImage->GetGraphicBuffer();
  void *pMem = nullptr;
  // Bug 1109957 size will be wrong if width or height are odd
  uint32_t size = aWidth * aHeight * 3 / 2;
  MOZ_ASSERT(!(aWidth & 1) && !(aHeight & 1));

  graphicBuffer->lock(GraphicBuffer::USAGE_SW_READ_MASK, &pMem);

  uint8_t* srcPtr = static_cast<uint8_t*>(pMem);

  // Create a video frame and append it to the track.
  RefPtr<layers::PlanarYCbCrImage> image = new GonkCameraImage();

  uint32_t dstWidth;
  uint32_t dstHeight;

  if (mRotation == 90 || mRotation == 270) {
    dstWidth = aHeight;
    dstHeight = aWidth;
  } else {
    dstWidth = aWidth;
    dstHeight = aHeight;
  }

  uint32_t half_width = dstWidth / 2;

  MOZ_ASSERT(mTextureClientAllocator);
  RefPtr<layers::TextureClient> textureClient
    = mTextureClientAllocator->CreateOrRecycle(gfx::SurfaceFormat::YUV,
                                               gfx::IntSize(dstWidth, dstHeight),
                                               layers::BackendSelector::Content,
                                               layers::TextureFlags::DEFAULT,
                                               layers::ALLOC_DISALLOW_BUFFERTEXTURECLIENT);
  if (textureClient) {
    android::sp<android::GraphicBuffer> destBuffer =
      static_cast<layers::GrallocTextureData*>(textureClient->GetInternalData())->GetGraphicBuffer();

    void* destMem = nullptr;
    destBuffer->lock(android::GraphicBuffer::USAGE_SW_WRITE_OFTEN, &destMem);
    uint8_t* dstPtr = static_cast<uint8_t*>(destMem);

    int32_t yStride = destBuffer->getStride();
    // Align to 16 bytes boundary
    int32_t uvStride = ((yStride / 2) + 15) & ~0x0F;

    libyuv::ConvertToI420(srcPtr, size,
                          dstPtr, yStride,
                          dstPtr + (yStride * dstHeight + (uvStride * dstHeight / 2)), uvStride,
                          dstPtr + (yStride * dstHeight), uvStride,
                          0, 0,
                          graphicBuffer->getStride(), aHeight,
                          aWidth, aHeight,
                          static_cast<libyuv::RotationMode>(mRotation),
                          libyuv::FOURCC_NV21);
    destBuffer->unlock();

    image->AsGrallocImage()->AdoptData(textureClient, gfx::IntSize(dstWidth, dstHeight));
  } else {
    // Handle out of gralloc case.
    image = mImageContainer->CreatePlanarYCbCrImage();
    uint8_t* dstPtr = image->AsPlanarYCbCrImage()->AllocateAndGetNewBuffer(size);

    libyuv::ConvertToI420(srcPtr, size,
                          dstPtr, dstWidth,
                          dstPtr + (dstWidth * dstHeight), half_width,
                          dstPtr + (dstWidth * dstHeight * 5 / 4), half_width,
                          0, 0,
                          graphicBuffer->getStride(), aHeight,
                          aWidth, aHeight,
                          static_cast<libyuv::RotationMode>(mRotation),
                          ConvertPixelFormatToFOURCC(graphicBuffer->getPixelFormat()));

    const uint8_t lumaBpp = 8;
    const uint8_t chromaBpp = 4;

    layers::PlanarYCbCrData data;
    data.mYChannel = dstPtr;
    data.mYSize = IntSize(dstWidth, dstHeight);
    data.mYStride = dstWidth * lumaBpp / 8;
    data.mCbCrStride = dstWidth * chromaBpp / 8;
    data.mCbChannel = dstPtr + dstHeight * data.mYStride;
    data.mCrChannel = data.mCbChannel + data.mCbCrStride * (dstHeight / 2);
    data.mCbCrSize = IntSize(dstWidth / 2, dstHeight / 2);
    data.mPicX = 0;
    data.mPicY = 0;
    data.mPicSize = IntSize(dstWidth, dstHeight);
    data.mStereoMode = StereoMode::MONO;

    image->AsPlanarYCbCrImage()->AdoptData(data);
  }
  graphicBuffer->unlock();

  // Implicitly releases last preview image.
  mImage = image.forget();
}