/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* Copyright 2012 Mozilla Foundation and Mozilla contributors * * 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 #include "android/log.h" #include "cutils/properties.h" #include "base/basictypes.h" #include "GonkCaptureProvider.h" #include "nsXULAppAPI.h" #include "nsStreamUtils.h" #include "nsThreadUtils.h" #include "RawStructs.h" #include "prinit.h" #define USE_GS2_LIBCAMERA #define CameraHardwareInterface CameraHardwareInterface_SGS2 #define HAL_openCameraHardware HAL_openCameraHardware_SGS2 #include "gonk/CameraHardwareInterface.h" #undef CameraHardwareInterface #undef USE_GS2_LIBCAMERA #undef HAL_openCameraHardware #undef ANDROID_HARDWARE_CAMERA_HARDWARE_INTERFACE_H // Needed to prevent a redefinition of 'struct android::image_rect_struct'... #define image_rect_type image_rect_type2 #define image_rect_struct image_rect_struct2 #define USE_MAGURO_LIBCAMERA #define CameraHardwareInterface CameraHardwareInterface_MAGURO #define HAL_openCameraHardware HAL_openCameraHardware_MAGURO #include "gonk/CameraHardwareInterface.h" #undef CameraHardwareInterface #undef USE_MAGURO_LIBCAMERA #undef HAL_openCameraHardware #undef ANDROID_HARDWARE_CAMERA_HARDWARE_INTERFACE_H #undef image_rect_type #undef image_rect_struct #define image_rect_type image_rect_type3 #define image_rect_struct image_rect_struct3 #define CameraHardwareInterface CameraHardwareInterface_DEFAULT #include "gonk/CameraHardwareInterface.h" #undef CameraHardwareInterface using namespace android; using namespace mozilla; class CameraHardwareInterface { public: enum Type { CAMERA_SGS2, CAMERA_MAGURO, CAMERA_DEFAULT }; static Type getType() { char propValue[PROPERTY_VALUE_MAX]; property_get("ro.product.board", propValue, NULL); if (!strcmp(propValue, "GT-I9100")) return CAMERA_SGS2; if (!strcmp(propValue, "msm7627a_sku1") || !strcmp(propValue, "MSM7627A_SKU3")) return CAMERA_MAGURO; printf_stderr("CameraHardwareInterface : unsupported camera for device %s\n", propValue); return CAMERA_DEFAULT; } static CameraHardwareInterface* openCamera(uint32_t aCamera); virtual ~CameraHardwareInterface() { } virtual bool ok() = 0; virtual void enableMsgType(int32_t msgType) = 0; virtual void disableMsgType(int32_t msgType) = 0; virtual bool msgTypeEnabled(int32_t msgType) = 0; virtual void setCallbacks(notify_callback notify_cb, data_callback data_cb, data_callback_timestamp data_cb_timestamp, void* user) = 0; virtual status_t startPreview() = 0; virtual void stopPreview() = 0; virtual void release() = 0; virtual status_t setParameters(const CameraParameters& params) = 0; virtual CameraParameters getParameters() const = 0; protected: CameraHardwareInterface(uint32_t aCamera = 0) { }; }; // Intentionally not trying to dlclose() this handle. That's playing // Russian roulette with security bugs. static void* sCameraLib; static PRCallOnceType sInitCameraLib; static PRStatus InitCameraLib() { sCameraLib = dlopen("/system/lib/libcamera.so", RTLD_LAZY); // We might fail to open the camera lib. That's OK. return PR_SUCCESS; } static void* GetCameraLibHandle() { PR_CallOnce(&sInitCameraLib, InitCameraLib); return sCameraLib; } template class CameraImpl : public CameraHardwareInterface { public: typedef sp (*HAL_openCameraHardware_DEFAULT)(int); typedef sp (*HAL_openCameraHardware_SGS2)(int); typedef sp (*HAL_openCameraHardware_MAGURO)(int, int); CameraImpl(uint32_t aCamera = 0) : mOk(false), mCamera(nullptr) { void* cameraLib = GetCameraLibHandle(); if (!cameraLib) { printf_stderr("CameraImpl: Failed to dlopen() camera library."); return; } void *hal = dlsym(cameraLib, "HAL_openCameraHardware"); HAL_openCameraHardware_DEFAULT funct0; HAL_openCameraHardware_SGS2 funct1; HAL_openCameraHardware_MAGURO funct2; switch(getType()) { case CAMERA_SGS2: funct1 = reinterpret_cast (hal); mCamera = funct1(aCamera); break; case CAMERA_MAGURO: funct2 = reinterpret_cast (hal); mCamera = funct2(aCamera, 1); break; case CAMERA_DEFAULT: funct0 = reinterpret_cast (hal); mCamera = funct0(aCamera); break; } mOk = mCamera != nullptr; if (!mOk) { printf_stderr("CameraImpl: HAL_openCameraHardware() returned NULL (no camera interface)."); } } bool ok() { return mOk; }; void enableMsgType(int32_t msgType) { mCamera->enableMsgType(msgType); }; void disableMsgType(int32_t msgType) { mCamera->disableMsgType(msgType); }; bool msgTypeEnabled(int32_t msgType) { return mCamera->msgTypeEnabled(msgType); }; void setCallbacks(notify_callback notify_cb, data_callback data_cb, data_callback_timestamp data_cb_timestamp, void* user) { mCamera->setCallbacks(notify_cb, data_cb, data_cb_timestamp, user); }; status_t startPreview() { return mCamera->startPreview(); }; void stopPreview() { mCamera->stopPreview(); }; void release() { return mCamera->release(); }; status_t setParameters(const CameraParameters& params) { return mCamera->setParameters(params); } CameraParameters getParameters() const { return mCamera->getParameters(); }; protected: bool mOk; sp mCamera; }; CameraHardwareInterface* CameraHardwareInterface::openCamera(uint32_t aCamera) { nsAutoPtr instance; switch(getType()) { case CAMERA_SGS2: instance = new CameraImpl(aCamera); break; case CAMERA_MAGURO: instance = new CameraImpl(aCamera); break; case CAMERA_DEFAULT: instance = new CameraImpl(aCamera); break; } if (!instance->ok()) { return nullptr; } return instance.forget(); }; // The maximum number of frames we keep in our queue. Don't live in the past. #define MAX_FRAMES_QUEUED 5 NS_IMPL_THREADSAFE_ISUPPORTS2(GonkCameraInputStream, nsIInputStream, nsIAsyncInputStream) GonkCameraInputStream::GonkCameraInputStream() : mAvailable(sizeof(RawVideoHeader)), mWidth(0), mHeight(0), mFps(30), mCamera(0), mHeaderSent(false), mClosed(true), mIs420p(false), mFrameSize(0), mMonitor("GonkCamera.Monitor") { } GonkCameraInputStream::~GonkCameraInputStream() { // clear the frame queue while (mFrameQueue.GetSize() > 0) { free(mFrameQueue.PopFront()); } // no need to close Close() since the stream is opened here : // http://mxr.mozilla.org/mozilla-central/source/netwerk/base/src/nsBaseChannel.cpp#239 // and this leads to http://mxr.mozilla.org/mozilla-central/source/netwerk/base/src/nsBaseChannel.cpp#259 // that creates and input pump with closeWhenDone == true // the pump cleans up properly at http://mxr.mozilla.org/mozilla-central/source/netwerk/base/src/nsInputStreamPump.cpp#565 } void GonkCameraInputStream::DataCallback(int32_t aMsgType, const sp& aDataPtr, void *aUser) { GonkCameraInputStream* stream = (GonkCameraInputStream*)(aUser); stream->ReceiveFrame((char*)aDataPtr->pointer(), aDataPtr->size()); } uint32_t GonkCameraInputStream::getNumberOfCameras() { typedef int (*HAL_getNumberOfCamerasFunct)(void); void* cameraLib = GetCameraLibHandle(); if (!cameraLib) return 0; void *hal = dlsym(cameraLib, "HAL_getNumberOfCameras"); if (nullptr == hal) return 0; HAL_getNumberOfCamerasFunct funct = reinterpret_cast (hal); return funct(); } NS_IMETHODIMP GonkCameraInputStream::Init(nsACString& aContentType, nsCaptureParams* aParams) { if (XRE_GetProcessType() != GeckoProcessType_Default) return NS_ERROR_NOT_IMPLEMENTED; mContentType = aContentType; mWidth = aParams->width; mHeight = aParams->height; mCamera = aParams->camera; uint32_t maxNumCameras = getNumberOfCameras(); if (maxNumCameras == 0) return NS_ERROR_FAILURE; if (mCamera >= maxNumCameras) mCamera = 0; mHardware = CameraHardwareInterface::openCamera(mCamera); if (!mHardware) return NS_ERROR_FAILURE; mHardware->setCallbacks(NULL, GonkCameraInputStream::DataCallback, NULL, this); mHardware->enableMsgType(CAMERA_MSG_PREVIEW_FRAME); CameraParameters params = mHardware->getParameters(); printf_stderr("Preview format : %s\n", params.get(params.KEY_SUPPORTED_PREVIEW_FORMATS)); Vector previewSizes; params.getSupportedPreviewSizes(previewSizes); // find the available preview size closest to the requested size. uint32_t minSizeDelta = UINT32_MAX; uint32_t bestWidth = mWidth; uint32_t bestHeight = mHeight; for (uint32_t i = 0; i < previewSizes.size(); i++) { Size size = previewSizes[i]; uint32_t delta = abs(size.width * size.height - mWidth * mHeight); if (delta < minSizeDelta) { minSizeDelta = delta; bestWidth = size.width; bestHeight = size.height; } } mWidth = bestWidth; mHeight = bestHeight; params.setPreviewSize(mWidth, mHeight); // try to set preferred image format params.setPreviewFormat("yuv420p"); params.setPreviewFrameRate(mFps); mHardware->setParameters(params); params = mHardware->getParameters(); mFps = params.getPreviewFrameRate(); mIs420p = !strcmp(params.getPreviewFormat(), "yuv420p"); mHardware->startPreview(); mClosed = false; return NS_OK; } void GonkCameraInputStream::ReceiveFrame(char* frame, uint32_t length) { { ReentrantMonitorAutoEnter enter(mMonitor); if (mFrameQueue.GetSize() > MAX_FRAMES_QUEUED) { free(mFrameQueue.PopFront()); mAvailable -= mFrameSize; } } mFrameSize = sizeof(RawPacketHeader) + length; char* fullFrame = (char*)moz_malloc(mFrameSize); if (!fullFrame) return; RawPacketHeader* header = reinterpret_cast (fullFrame); header->packetID = 0xFF; header->codecID = RAW_ID; if (mIs420p) { memcpy(fullFrame + sizeof(RawPacketHeader), frame, length); } else { // we copy the Y plane, and de-interlace the CrCb uint32_t yFrameSize = mWidth * mHeight; uint32_t uvFrameSize = yFrameSize / 4; memcpy(fullFrame + sizeof(RawPacketHeader), frame, yFrameSize); char* uFrame = fullFrame + sizeof(RawPacketHeader) + yFrameSize; char* vFrame = fullFrame + sizeof(RawPacketHeader) + yFrameSize + uvFrameSize; const char* yFrame = frame + yFrameSize; for (uint32_t i = 0; i < uvFrameSize; i++) { uFrame[i] = yFrame[2 * i + 1]; vFrame[i] = yFrame[2 * i]; } } { ReentrantMonitorAutoEnter enter(mMonitor); mAvailable += mFrameSize; mFrameQueue.Push((void*)fullFrame); } NotifyListeners(); } NS_IMETHODIMP GonkCameraInputStream::Available(uint64_t *aAvailable) { ReentrantMonitorAutoEnter enter(mMonitor); *aAvailable = mAvailable; return NS_OK; } NS_IMETHODIMP GonkCameraInputStream::IsNonBlocking(bool *aNonBlock) { *aNonBlock = true; return NS_OK; } NS_IMETHODIMP GonkCameraInputStream::Read(char *aBuffer, uint32_t aCount, uint32_t *aRead) { return ReadSegments(NS_CopySegmentToBuffer, aBuffer, aCount, aRead); } NS_IMETHODIMP GonkCameraInputStream::ReadSegments(nsWriteSegmentFun aWriter, void *aClosure, uint32_t aCount, uint32_t *aRead) { *aRead = 0; nsresult rv; if (mAvailable == 0) return NS_BASE_STREAM_WOULD_BLOCK; if (aCount > mAvailable) aCount = mAvailable; if (!mHeaderSent) { RawVideoHeader header; header.headerPacketID = 0; header.codecID = RAW_ID; header.majorVersion = 0; header.minorVersion = 1; header.options = 1 | 1 << 1; // color, 4:2:0 header.alphaChannelBpp = 0; header.lumaChannelBpp = 8; header.chromaChannelBpp = 4; header.colorspace = 1; header.frameWidth = mWidth; header.frameHeight = mHeight; header.aspectNumerator = 1; header.aspectDenominator = 1; header.framerateNumerator = mFps; header.framerateDenominator = 1; rv = aWriter(this, aClosure, (const char*)&header, 0, sizeof(RawVideoHeader), aRead); if (NS_FAILED(rv)) return NS_OK; mHeaderSent = true; aCount -= sizeof(RawVideoHeader); mAvailable -= sizeof(RawVideoHeader); } { ReentrantMonitorAutoEnter enter(mMonitor); while ((mAvailable > 0) && (aCount >= mFrameSize)) { uint32_t readThisTime = 0; char* frame = (char*)mFrameQueue.PopFront(); rv = aWriter(this, aClosure, (const char*)frame, *aRead, mFrameSize, &readThisTime); if (readThisTime != mFrameSize) { mFrameQueue.PushFront((void*)frame); return NS_OK; } // RawReader does a copy when calling VideoData::Create() free(frame); if (NS_FAILED(rv)) return NS_OK; aCount -= readThisTime; mAvailable -= readThisTime; *aRead += readThisTime; } } return NS_OK; } NS_IMETHODIMP GonkCameraInputStream::Close() { return CloseWithStatus(NS_OK); } void GonkCameraInputStream::doClose() { ReentrantMonitorAutoEnter enter(mMonitor); if (mClosed) return; mHardware->disableMsgType(CAMERA_MSG_ALL_MSGS); mHardware->stopPreview(); mHardware->release(); delete mHardware; mClosed = true; } void GonkCameraInputStream::NotifyListeners() { ReentrantMonitorAutoEnter enter(mMonitor); if (mCallback && (mAvailable > sizeof(RawVideoHeader))) { nsCOMPtr callback; if (mCallbackTarget) { NS_NewInputStreamReadyEvent(getter_AddRefs(callback), mCallback, mCallbackTarget); } else { callback = mCallback; } NS_ASSERTION(callback, "Shouldn't fail to make the callback!"); // Null the callback first because OnInputStreamReady may reenter AsyncWait mCallback = nullptr; mCallbackTarget = nullptr; callback->OnInputStreamReady(this); } } NS_IMETHODIMP GonkCameraInputStream::AsyncWait(nsIInputStreamCallback *aCallback, uint32_t aFlags, uint32_t aRequestedCount, nsIEventTarget *aTarget) { if (aFlags != 0) return NS_ERROR_NOT_IMPLEMENTED; if (mCallback || mCallbackTarget) return NS_ERROR_UNEXPECTED; mCallbackTarget = aTarget; mCallback = aCallback; // What we are being asked for may be present already NotifyListeners(); return NS_OK; } NS_IMETHODIMP GonkCameraInputStream::CloseWithStatus(nsresult status) { GonkCameraInputStream::doClose(); return NS_OK; } /** * GonkCaptureProvider implementation */ NS_IMPL_ISUPPORTS0(GonkCaptureProvider) GonkCaptureProvider* GonkCaptureProvider::sInstance = NULL; GonkCaptureProvider::GonkCaptureProvider() { } GonkCaptureProvider::~GonkCaptureProvider() { GonkCaptureProvider::sInstance = NULL; } nsresult GonkCaptureProvider::Init(nsACString& aContentType, nsCaptureParams* aParams, nsIInputStream** aStream) { NS_ENSURE_ARG_POINTER(aParams); NS_ASSERTION(aParams->frameLimit == 0 || aParams->timeLimit == 0, "Cannot set both a frame limit and a time limit!"); nsRefPtr stream; if (aContentType.EqualsLiteral("video/x-raw-yuv")) { stream = new GonkCameraInputStream(); if (stream) { nsresult rv = stream->Init(aContentType, aParams); if (NS_FAILED(rv)) return rv; } else { return NS_ERROR_OUT_OF_MEMORY; } } else { NS_NOTREACHED("Should not have asked Gonk for this type!"); } return CallQueryInterface(stream, aStream); } already_AddRefed GetGonkCaptureProvider() { if (!GonkCaptureProvider::sInstance) { GonkCaptureProvider::sInstance = new GonkCaptureProvider(); } GonkCaptureProvider::sInstance->AddRef(); return GonkCaptureProvider::sInstance; }