/* * Copyright (C) 2012 Mozilla Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "VideoUtils.h" #include "GonkCameraHwMgr.h" #include "GonkCameraPreview.h" #define DOM_CAMERA_LOG_LEVEL 2 #include "CameraCommon.h" using namespace mozilla; /** * This big macro takes two 32-bit input blocks of interlaced u and * v data (from a yuv420sp frame) in 's0' and 's1', and deinterlaces * them into pairs of contiguous 32-bit blocks, the u plane data in * 'u', and the v plane data in 'v' (i.e. for a yuv420p frame). * * yuv420sp: * [ y-data ][ uv-data ] * [ uv-data ]: [u0][v0][u1][v1][u2][v2]... * * yuv420p: * [ y-data ][ u-data ][ v-data ] * [ u-data ]: [u0][u1][u2]... * [ v-data ]: [v0][v1][v2]... * * Doing this in 32-bit blocks is significantly faster than using * byte-wise operations on ARM. (In some cases, the byte-wise * de-interlacing can be too slow to keep up with the preview frames * coming from the driver. */ #define DEINTERLACE( u, v, s0, s1 ) \ u = ( (s0) & 0xFF00UL ) >> 8 | ( (s0) & 0xFF000000UL ) >> 16; \ u |= ( (s1) & 0xFF00UL ) << 8 | ( (s1) & 0xFF000000UL ); \ v = ( (s0) & 0xFFUL ) | ( (s0) & 0xFF0000UL ) >> 8; \ v |= ( (s1) & 0xFFUL ) << 16 | ( (s1) & 0xFF0000UL ) << 8; void GonkCameraPreview::ReceiveFrame(PRUint8 *aData, PRUint32 aLength) { DOM_CAMERA_LOGI("%s:%d : this=%p\n", __func__, __LINE__, this); if (mInput->HaveEnoughBuffered(TRACK_VIDEO)) { if (mDiscardedFrameCount == 0) { DOM_CAMERA_LOGI("mInput has enough data buffered, starting to discard\n"); } ++mDiscardedFrameCount; return; } else if (mDiscardedFrameCount) { DOM_CAMERA_LOGI("mInput needs more data again; discarded %d frames in a row\n", mDiscardedFrameCount); mDiscardedFrameCount = 0; } switch (mFormat) { case GonkCameraHardware::PREVIEW_FORMAT_YUV420SP: { // de-interlace the u and v planes uint8_t* y = aData; uint32_t yN = mWidth * mHeight; NS_ASSERTION((yN & 0x3) == 0, "Invalid image dimensions!"); uint32_t uvN = yN / 4; uint32_t* src = (uint32_t*)( y + yN ); uint32_t* d = new uint32_t[ uvN / 2 ]; uint32_t* u = d; uint32_t* v = u + uvN / 4; // we're handling pairs of 32-bit words, so divide by 8 NS_ASSERTION((uvN & 0x7) == 0, "Invalid image dimensions!"); uvN /= 8; while (uvN--) { uint32_t src0 = *src++; uint32_t src1 = *src++; uint32_t u0; uint32_t v0; uint32_t u1; uint32_t v1; DEINTERLACE( u0, v0, src0, src1 ); src0 = *src++; src1 = *src++; DEINTERLACE( u1, v1, src0, src1 ); *u++ = u0; *u++ = u1; *v++ = v0; *v++ = v1; } memcpy(y + yN, d, yN / 2); delete[] d; } break; case GonkCameraHardware::PREVIEW_FORMAT_YUV420P: // no transformating required break; default: // in a format we don't handle, get out of here return; } Image::Format format = Image::PLANAR_YCBCR; nsRefPtr image = mImageContainer->CreateImage(&format, 1); image->AddRef(); PlanarYCbCrImage* videoImage = static_cast(image.get()); /** * If you change either lumaBpp or chromaBpp, make sure the * assertions below still hold. */ const PRUint8 lumaBpp = 8; const PRUint8 chromaBpp = 4; PlanarYCbCrImage::Data data; data.mYChannel = aData; data.mYSize = gfxIntSize(mWidth, mHeight); data.mYStride = mWidth * lumaBpp; NS_ASSERTION((data.mYStride & 0x7) == 0, "Invalid image dimensions!"); data.mYStride /= 8; data.mCbCrStride = mWidth * chromaBpp; NS_ASSERTION((data.mCbCrStride & 0x7) == 0, "Invalid image dimensions!"); data.mCbCrStride /= 8; data.mCbChannel = aData + mHeight * data.mYStride; data.mCrChannel = data.mCbChannel + mHeight * data.mCbCrStride / 2; data.mCbCrSize = gfxIntSize(mWidth / 2, mHeight / 2); data.mPicX = 0; data.mPicY = 0; data.mPicSize = gfxIntSize(mWidth, mHeight); data.mStereoMode = mozilla::layers::STEREO_MODE_MONO; videoImage->SetData(data); // copies buffer mVideoSegment.AppendFrame(videoImage, 1, gfxIntSize(mWidth, mHeight)); mInput->AppendToTrack(TRACK_VIDEO, &mVideoSegment); mFrameCount += 1; if ((mFrameCount % 10) == 0) { DOM_CAMERA_LOGI("%s:%d : mFrameCount = %d\n", __func__, __LINE__, mFrameCount); } } nsresult GonkCameraPreview::StartImpl() { DOM_CAMERA_LOGI("%s:%d : this=%p\n", __func__, __LINE__, this); /** * We set and then immediately get the preview size, in case the camera * driver has decided to ignore our given dimensions. We need to know * the dimensions the driver is using so that, if needed, we can properly * de-interlace the yuv420sp format in ReceiveFrame() above. */ GonkCameraHardware::SetPreviewSize(mHwHandle, mWidth, mHeight); GonkCameraHardware::GetPreviewSize(mHwHandle, &mWidth, &mHeight); SetFrameRate(GonkCameraHardware::GetFps(mHwHandle)); if (GonkCameraHardware::StartPreview(mHwHandle) != OK) { DOM_CAMERA_LOGE("%s: failed to start preview\n", __func__); return NS_ERROR_FAILURE; } // GetPreviewFormat() must be called after StartPreview(). mFormat = GonkCameraHardware::GetPreviewFormat(mHwHandle); DOM_CAMERA_LOGI("preview stream is (actually!) %d x %d (w x h), %d frames per second, format %d\n", mWidth, mHeight, mFramesPerSecond, mFormat); return NS_OK; } nsresult GonkCameraPreview::StopImpl() { DOM_CAMERA_LOGI("%s:%d : this=%p\n", __func__, __LINE__, this); GonkCameraHardware::StopPreview(mHwHandle); mInput->EndTrack(TRACK_VIDEO); mInput->Finish(); return NS_OK; }