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(); }