/* * Copyright (C) 2011 The Android Open Source Project * * 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. */ #ifndef HW_EMULATOR_CAMERA_EMULATED_CAMERA_DEVICE_H #define HW_EMULATOR_CAMERA_EMULATED_CAMERA_DEVICE_H /* * Contains declaration of an abstract class EmulatedCameraDevice that defines * functionality expected from an emulated physical camera device: * - Obtaining and setting camera device parameters * - Capturing frames * - Streaming video * - etc. */ #include <utils/threads.h> #include <utils/KeyedVector.h> #include <utils/String8.h> #include "EmulatedCameraCommon.h" #include "Converters.h" #include "WorkerThread.h" #undef min #undef max #include <vector> namespace android { class EmulatedCamera; /* Encapsulates an abstract class EmulatedCameraDevice that defines * functionality expected from an emulated physical camera device: * - Obtaining and setting camera device parameters * - Capturing frames * - Streaming video * - etc. */ class EmulatedCameraDevice { public: /* Constructs EmulatedCameraDevice instance. * Param: * camera_hal - Emulated camera that implements the camera HAL API, and * manages (contains) this object. */ explicit EmulatedCameraDevice(EmulatedCamera* camera_hal); /* Destructs EmulatedCameraDevice instance. */ virtual ~EmulatedCameraDevice(); /*************************************************************************** * Emulated camera device abstract interface **************************************************************************/ public: /* Connects to the camera device. * This method must be called on an initialized instance of this class. * Return: * NO_ERROR on success, or an appropriate error status. */ virtual status_t connectDevice() = 0; /* Disconnects from the camera device. * Return: * NO_ERROR on success, or an appropriate error status. If this method is * called for already disconnected, or uninitialized instance of this class, * a successful status must be returned from this method. If this method is * called for an instance that is in the "started" state, this method must * return a failure. */ virtual status_t disconnectDevice() = 0; /* Starts the camera device. * This method tells the camera device to start capturing frames of the given * dimensions for the given pixel format. Note that this method doesn't start * the delivery of the captured frames to the emulated camera. Call * startDeliveringFrames method to start delivering frames. This method must * be called on a connected instance of this class. If it is called on a * disconnected instance, this method must return a failure. * Param: * width, height - Frame dimensions to use when capturing video frames. * pix_fmt - Pixel format to use when capturing video frames. * Return: * NO_ERROR on success, or an appropriate error status. */ virtual status_t startDevice(int width, int height, uint32_t pix_fmt) = 0; /* Stops the camera device. * This method tells the camera device to stop capturing frames. Note that * this method doesn't stop delivering frames to the emulated camera. Always * call stopDeliveringFrames prior to calling this method. * Return: * NO_ERROR on success, or an appropriate error status. If this method is * called for an object that is not capturing frames, or is disconnected, * or is uninitialized, a successful status must be returned from this * method. */ virtual status_t stopDevice() = 0; /*************************************************************************** * Emulated camera device public API **************************************************************************/ public: /* Initializes EmulatedCameraDevice instance. * Derived classes should override this method in order to cache static * properties of the physical device (list of supported pixel formats, frame * sizes, etc.) If this method is called on an already initialized instance, * it must return a successful status. * Return: * NO_ERROR on success, or an appropriate error status. */ virtual status_t Initialize(); /* Initializes the white balance modes parameters. * The parameters are passed by each individual derived camera API to * represent that different camera manufacturers may have different * preferences on the white balance parameters. Green channel in the RGB * color space is fixed to keep the luminance to be reasonably constant. * * Param: * mode the text describing the current white balance mode * r_scale the scale factor for the R channel in RGB space * b_scale the scale factor for the B channel in RGB space. */ void initializeWhiteBalanceModes(const char* mode, const float r_scale, const float b_scale); /* Starts delivering frames captured from the camera device. * This method will start the worker thread that would be pulling frames from * the camera device, and will deliver the pulled frames back to the emulated * camera via onNextFrameAvailable callback. This method must be called on a * connected instance of this class with a started camera device. If it is * called on a disconnected instance, or camera device has not been started, * this method must return a failure. * Param: * one_burst - Controls how many frames should be delivered. If this * parameter is 'true', only one captured frame will be delivered to the * emulated camera. If this parameter is 'false', frames will keep * coming until stopDeliveringFrames method is called. Typically, this * parameter is set to 'true' only in order to obtain a single frame * that will be used as a "picture" in takePicture method of the * emulated camera. * Return: * NO_ERROR on success, or an appropriate error status. */ virtual status_t startDeliveringFrames(bool one_burst); /* Stops delivering frames captured from the camera device. * This method will stop the worker thread started by startDeliveringFrames. * Return: * NO_ERROR on success, or an appropriate error status. */ virtual status_t stopDeliveringFrames(); /* Set the preview frame rate. * Indicates the rate at which the camera should provide preview frames in * frames per second. */ status_t setPreviewFrameRate(int framesPerSecond); /* Sets the exposure compensation for the camera device. */ void setExposureCompensation(const float ev); /* Sets the white balance mode for the device. */ void setWhiteBalanceMode(const char* mode); /* Gets current framebuffer in a selected format * This method must be called on a connected instance of this class with a * started camera device. If it is called on a disconnected instance, or * camera device has not been started, this method must return a failure. * Note that this method should be called only after at least one frame has * been captured and delivered. Otherwise it will return garbage in the * preview frame buffer. Typically, this method should be called from * onNextFrameAvailable callback. The method can perform some basic pixel * format conversion for the most efficient conversions. If a conversion * is not supported the method will fail. Note that this does NOT require * that the current frame be locked using a FrameLock object. * * Param: * buffer - Buffer, large enough to contain the entire frame. * pixelFormat - The pixel format to convert to, use * getOriginalPixelFormat() to get the configured pixel * format (if using this no conversion will be needed) * timestamp - Receives the timestamp at which the preview frame was * generated. * Return: * NO_ERROR on success, or an appropriate error status. */ virtual status_t getCurrentFrame(void* buffer, uint32_t pixelFormat, int64_t* timestamp); /* Gets current framebuffer, converted into preview frame format. * This method must be called on a connected instance of this class with a * started camera device. If it is called on a disconnected instance, or * camera device has not been started, this method must return a failure. * Note that this method should be called only after at least one frame has * been captured and delivered. Otherwise it will return garbage in the * preview frame buffer. Typically, this method should be called from * onNextFrameAvailable callback. Note that this does NOT require that the * current frame be locked using a FrameLock object. * Param: * buffer - Buffer, large enough to contain the entire preview frame. * timestamp - Receives the timestamp at which the preview frame was * generated. * Return: * NO_ERROR on success, or an appropriate error status. */ virtual status_t getCurrentPreviewFrame(void* buffer, int64_t* timestamp); /* Gets a pointer to the current frame buffer in its raw format. * This method must be called on a connected instance of this class with a * started camera device. If it is called on a disconnected instance, or * camera device has not been started, this method must return NULL. * This method should only be called when the frame lock is held through * a FrameLock object. Otherwise the contents of the frame might change * unexpectedly or its memory could be deallocated leading to a crash. * Return: * A pointer to the current frame buffer on success, NULL otherwise. */ virtual const void* getCurrentFrame(); class FrameLock { public: FrameLock(EmulatedCameraDevice& cameraDevice); ~FrameLock(); private: EmulatedCameraDevice& mCameraDevice; }; /* Gets width of the frame obtained from the physical device. * Return: * Width of the frame obtained from the physical device. Note that value * returned from this method is valid only in case if camera device has been * started. */ inline int getFrameWidth() const { ALOGE_IF(!isStarted(), "%s: Device is not started", __FUNCTION__); return mFrameWidth; } /* Gets height of the frame obtained from the physical device. * Return: * Height of the frame obtained from the physical device. Note that value * returned from this method is valid only in case if camera device has been * started. */ inline int getFrameHeight() const { ALOGE_IF(!isStarted(), "%s: Device is not started", __FUNCTION__); return mFrameHeight; } /* Gets byte size of the current frame buffer. * Return: * Byte size of the frame buffer. Note that value returned from this method * is valid only in case if camera device has been started. */ inline size_t getFrameBufferSize() const { ALOGE_IF(!isStarted(), "%s: Device is not started", __FUNCTION__); return mFrameBufferSize; } /* Get number of bytes required to store current video frame buffer. Note * that this can be different from getFrameBufferSize depending on the pixel * format and resolution. The video frames use a pixel format that is * suitable for the encoding pipeline and this may have different alignment * requirements than the pixel format used for regular frames. */ inline size_t getVideoFrameBufferSize() const { ALOGE_IF(!isStarted(), "%s: Device is not started", __FUNCTION__); // Currently the video format is always YUV 420 without any kind of // alignment. So each pixel uses 12 bits, and then we divide by 8 to get // the size in bytes. If additional pixel formats are supported this // should be updated to take the selected video format into // consideration. return (mFrameWidth * mFrameHeight * 12) / 8; } /* Gets number of pixels in the current frame buffer. * Return: * Number of pixels in the frame buffer. Note that value returned from this * method is valid only in case if camera device has been started. */ inline int getPixelNum() const { ALOGE_IF(!isStarted(), "%s: Device is not started", __FUNCTION__); return mTotalPixels; } /* Gets pixel format of the frame that camera device streams to this class. * Throughout camera framework, there are three different forms of pixel * format representation: * - Original format, as reported by the actual camera device. Values for * this format are declared in bionic/libc/kernel/common/linux/videodev2.h * - String representation as defined in CameraParameters::PIXEL_FORMAT_XXX * strings in frameworks/base/include/camera/CameraParameters.h * - HAL_PIXEL_FORMAT_XXX format, as defined in system/core/include/system/graphics.h * Since emulated camera device gets its data from the actual device, it gets * pixel format in the original form. And that's the pixel format * representation that will be returned from this method. HAL components will * need to translate value returned from this method to the appropriate form. * This method must be called only on started instance of this class, since * it's applicable only when camera device is ready to stream frames. * Param: * pix_fmt - Upon success contains the original pixel format. * Return: * Current framebuffer's pixel format. Note that value returned from this * method is valid only in case if camera device has been started. */ inline uint32_t getOriginalPixelFormat() const { ALOGE_IF(!isStarted(), "%s: Device is not started", __FUNCTION__); return mPixelFormat; } /* * State checkers. */ inline bool isInitialized() const { return mState != ECDS_CONSTRUCTED; } inline bool isConnected() const { /* Instance is connected when its status is either"connected", or * "started". */ return mState == ECDS_CONNECTED || mState == ECDS_STARTED; } inline bool isStarted() const { return mState == ECDS_STARTED; } /* Enable auto-focus for the camera, this is only possible between calls to * startPreview and stopPreview, i.e. when preview frames are being * delivered. This will eventually trigger a callback to the camera HAL * saying auto-focus completed. */ virtual status_t setAutoFocus(); /* Cancel auto-focus if it's enabled. */ virtual status_t cancelAutoFocus(); /* Request an asynchronous camera restart with new image parameters. The * restart will be performed on the same thread that delivers frames, * ensuring that all callbacks are done from the same thread. * Return * false if the thread request cannot be honored because no thread is * running or some other error occured. */ bool requestRestart(int width, int height, uint32_t pixelFormat, bool takingPicture, bool oneBurst); /**************************************************************************** * Emulated camera device private API ***************************************************************************/ protected: /* Performs common validation and calculation of startDevice parameters. * Param: * width, height, pix_fmt - Parameters passed to the startDevice method. * Return: * NO_ERROR on success, or an appropriate error status. */ virtual status_t commonStartDevice(int width, int height, uint32_t pix_fmt); /* Performs common cleanup on stopDevice. * This method will undo what commonStartDevice had done. */ virtual void commonStopDevice(); /** Computes a luminance value after taking the exposure compensation. * value into account. * * Param: * inputY - The input luminance value. * Return: * The luminance value after adjusting the exposure compensation. */ inline uint8_t changeExposure(const uint8_t& inputY) const { return static_cast<uint8_t>(clamp(static_cast<float>(inputY) * mExposureCompensation)); } /** Computes the pixel value in YUV space after adjusting to the current * white balance mode. */ void changeWhiteBalance(uint8_t& y, uint8_t& u, uint8_t& v) const; /* Check if there is a pending auto-focus trigger and send a notification * if there is. This should be called from the worker thread loop if the * camera device wishes to use the default behavior of immediately sending * an auto-focus completion event on request. Otherwise the device should * implement its own auto-focus behavior. */ void checkAutoFocusTrigger(); /* Implementation for getCurrentFrame that includes pixel format conversion * if needed. This allows subclasses to easily use this method instead of * having to reimplement the conversion all over. */ status_t getCurrentFrameImpl(const uint8_t* source, uint8_t* dest, uint32_t pixelFormat) const; /**************************************************************************** * Worker thread management. * Typicaly when emulated camera device starts capturing frames from the * actual device, it does that in a worker thread created in StartCapturing, * and terminated in StopCapturing. Since this is such a typical scenario, * it makes sence to encapsulate worker thread management in the base class * for all emulated camera devices. ***************************************************************************/ protected: /* Starts the worker thread. * Typically, the worker thread is started from the startDeliveringFrames * method of this class. * Param: * one_burst - Controls how many times thread loop should run. If this * parameter is 'true', thread routine will run only once If this * parameter is 'false', thread routine will run until * stopWorkerThreads method is called. See startDeliveringFrames for * more info. * Return: * NO_ERROR on success, or an appropriate error status. */ virtual status_t startWorkerThread(bool one_burst); /* Stop the worker thread. * Note that this method will always wait for the worker thread to * terminate. Typically, the worker thread is stopped from the * stopDeliveringFrames method of this class. * Return: * NO_ERROR on success, or an appropriate error status. */ virtual status_t stopWorkerThread(); /* Produce a camera frame and place it in buffer. The buffer is one of * the two buffers provided to mFrameProducer during construction along with * a pointer to this method. The method is expected to know what size frames * it provided to the producer thread. Returning false indicates an * unrecoverable error that will stop the frame production thread. */ virtual bool produceFrame(void* buffer, int64_t* timestamp) = 0; /* Get the primary buffer to use when constructing the FrameProducer. */ virtual void* getPrimaryBuffer() { return mFrameBuffers[0].data(); } /* Get the seconary buffer to use when constructing the FrameProducer. */ virtual void* getSecondaryBuffer() { return mFrameBuffers[1].data(); } /* A class that encaspulates the asynchronous behavior of a camera. This * includes asynchronous production (through another thread), frame delivery * as well as asynchronous state changes that have to be synchronized with * frame production and delivery but can't be blocking the camera HAL. */ class CameraThread : public WorkerThread { public: typedef bool (*ProduceFrameFunc)(void* opaque, void* destinationBuffer, int64_t* destinationTimestamp); CameraThread(EmulatedCameraDevice* cameraDevice, ProduceFrameFunc producer, void* producerOpaque); /* Access the primary buffer of the frame producer, this is the frame * that is currently not being written to. The buffer will only have * valid contents if hasFrame() returns true. Note that accessing this * without first having created a Lock can lead to contents changing * without notice. */ const void* getPrimaryBuffer() const; int64_t getPrimaryTimestamp() const; /* Lock and unlock the primary buffer */ void lockPrimaryBuffer(); void unlockPrimaryBuffer(); void requestRestart(int width, int height, uint32_t pixelFormat, bool takingPicture, bool oneBurst); private: bool checkRestartRequest(); bool waitForFrameOrTimeout(nsecs_t timeout); bool inWorkerThread() override; status_t onThreadStart() override; void onThreadExit() override; /* A class with a thread that will call a function at a specified * interval to produce frames. This is done in a double-buffered fashion * to make sure that one of the frames can be delivered without risk of * overwriting its contents. Access to the primary buffer, the one NOT * being drawn to, should be protected with the lock methods provided or * the guarantee of not overwriting the contents does not hold. */ class FrameProducer : public WorkerThread { public: FrameProducer(EmulatedCameraDevice* cameraDevice, ProduceFrameFunc producer, void* opaque, void* primaryBuffer, void* secondaryBuffer); /* Indicates if the producer has produced at least one frame. */ bool hasFrame() const; const void* getPrimaryBuffer() const; int64_t getPrimaryTimestamp() const; void lockPrimaryBuffer(); void unlockPrimaryBuffer(); protected: bool inWorkerThread() override; ProduceFrameFunc mProducer; void* mOpaque; void* mPrimaryBuffer; void* mSecondaryBuffer; int64_t mPrimaryTimestamp; int64_t mSecondaryTimestamp; nsecs_t mLastFrame; mutable Mutex mBufferMutex; std::atomic<bool> mHasFrame; }; nsecs_t mCurFrameTimestamp; /* Worker thread that will produce frames for the camera thread */ sp<FrameProducer> mFrameProducer; ProduceFrameFunc mProducerFunc; void* mProducerOpaque; Mutex mRequestMutex; int mRestartWidth; int mRestartHeight; uint32_t mRestartPixelFormat; bool mRestartOneBurst; bool mRestartTakingPicture; bool mRestartRequested; }; /**************************************************************************** * Data members ***************************************************************************/ protected: /* Locks this instance for parameters, state, etc. change. */ Mutex mObjectLock; /* A camera thread that is used in frame production, delivery and handling * of asynchronous restarts. Internally the process of generating and * delivering frames is split up into two threads. This way frames can * always be delivered on time even if they cannot be produced fast enough * to keep up with the expected frame rate. It also increases performance on * multi-core systems. If the producer cannot keep up the last frame will * simply be delivered again. */ sp<CameraThread> mCameraThread; /* Emulated camera object containing this instance. */ EmulatedCamera* mCameraHAL; /* Framebuffers containing the frame being drawn to and the frame being * delivered. This is used by the double buffering producer thread and * the consumer thread will copy frames from one of these buffers to * mCurrentFrame to avoid being stalled by frame production. */ std::vector<uint8_t> mFrameBuffers[2]; /* * Framebuffer properties. */ /* Byte size of the framebuffer. */ size_t mFrameBufferSize; /* Original pixel format (one of the V4L2_PIX_FMT_XXX values, as defined in * bionic/libc/kernel/common/linux/videodev2.h */ uint32_t mPixelFormat; /* Frame width */ int mFrameWidth; /* Frame height */ int mFrameHeight; /* The number of frames per second that the camera should deliver */ int mFramesPerSecond; /* Defines byte distance between the start of each Y row */ int mYStride; /* Defines byte distance between the start of each U/V row. For formats with * separate U and V planes this is the distance between rows in each plane. * For formats with interleaved U and V components this is the distance * between rows in the interleaved plane, meaning that it's the stride over * the combined U and V components. */ int mUVStride; /* Total number of pixels */ int mTotalPixels; /* Exposure compensation value */ float mExposureCompensation; float* mWhiteBalanceScale; DefaultKeyedVector<String8, float*> mSupportedWhiteBalanceScale; /* Defines possible states of the emulated camera device object. */ enum EmulatedCameraDeviceState { /* Object has been constructed. */ ECDS_CONSTRUCTED, /* Object has been initialized. */ ECDS_INITIALIZED, /* Object has been connected to the physical device. */ ECDS_CONNECTED, /* Camera device has been started. */ ECDS_STARTED, }; /* Object state. */ EmulatedCameraDeviceState mState; private: /* Lock the current frame so that it can safely be accessed using * getCurrentFrame. Prefer using a FrameLock object on the stack instead * to ensure that the lock is always unlocked properly. */ void lockCurrentFrame(); /* Unlock the current frame after locking it. Prefer using a FrameLock * object instead. */ void unlockCurrentFrame(); static bool staticProduceFrame(void* opaque, void* buffer, int64_t* timestamp) { auto cameraDevice = reinterpret_cast<EmulatedCameraDevice*>(opaque); return cameraDevice->produceFrame(buffer, timestamp); } /* A flag indicating if an auto-focus completion event should be sent the * next time the worker thread runs. This implies that auto-focus completion * event can only be delivered while preview frames are being delivered. * This is also a requirement specified in the documentation where a request * to perform auto-focusing is only valid between calls to startPreview and * stopPreview. * https://developer.android.com/reference/android/hardware/Camera.html#autoFocus(android.hardware.Camera.AutoFocusCallback) */ std::atomic<bool> mTriggerAutoFocus; }; }; /* namespace android */ #endif /* HW_EMULATOR_CAMERA_EMULATED_CAMERA_DEVICE_H */