From 8508f3e858efeb841e67b30c99b30ad5cbbe916c Mon Sep 17 00:00:00 2001 From: annie_wangli Date: Sun, 10 Apr 2022 14:37:01 +0800 Subject: [PATCH] update docs Signed-off-by: annie_wangli --- .../driver/driver-peripherals-camera-des.md | 1295 +++++++++-------- .../camera-driver-model-architecture.png | Bin 34752 -> 27028 bytes 2 files changed, 682 insertions(+), 613 deletions(-) diff --git a/en/device-dev/driver/driver-peripherals-camera-des.md b/en/device-dev/driver/driver-peripherals-camera-des.md index cca003d566..51667b52f5 100644 --- a/en/device-dev/driver/driver-peripherals-camera-des.md +++ b/en/device-dev/driver/driver-peripherals-camera-des.md @@ -1,670 +1,739 @@ -# Camera +# Camera -## Overview - -The OpenHarmony camera driver model implements the camera hardware driver interface \(HDI\) and the camera pipeline model to manage camera devices. +## Overview +### Camera +The OpenHarmony camera driver model implements the camera hardware device interface (HDI) and the camera pipeline model to manage camera devices. The camera driver model consists of the following layers: -- HDI implementation layer: implements standard southbound interfaces of OpenHarmony cameras. ++ HDI implementation layer: implements standard ohos (OpenHarmony operating system) APIs for cameras. ++ Framework layer: connects to the HDI implementation layer for control instruction and stream transfer, establishes data channels, and manages camera devices. ++ Device adaptation layer: shields the differences between underlying chips and OSs for multi-platform adaptation. -- Framework layer: connects to the HDI implementation layer for control instruction and stream transfer, establishes data channels, and manages camera devices. +### Working Principles -- Adaptation layer: shields the differences between bottom-layer chips and OSs for multi-platform adaptation. +The camera module is used to initialize services and devices, set up data channels, and configure, create, deliver, and capture streams. The figure below illustrates camera driver model. +**Figure 1** HDF-based camera driver model -**Figure 1** Architecture of the camera driver model -![](figures/camera-driver-model-architecture.png "camera-driver-model-architecture") +![](figures/camera-driver-model-architecture.png) -1. The CameraDeviceHost process is created during system startup. The process enumerates underlying devices, creates a **DeviceManager** instance that manages the device tree, an object for each underlying device, and a **CameraHost** instance, and registers the **CameraHost** instance with the UHDF service. Through this service, the upper layer can obtain the CameraDeviceHost process at the bottom layer to operate the underlying devices. Note that the **DeviceManager** instance can also be created by using the configuration table. +1. When the system starts, the camera_host process is created. The process enumerates underlying devices, creates a **DeviceManager** instance that manages the device tree, an object for each underlying device, and a **CameraHost** instance, and registers the **CameraHost** instance with the UHDF service. Through the UHDF service, the camera service can obtain the underlying **CameraDeviceHost** services to operate the hardware devices. Note that the **DeviceManager** instance can also be created by using the configuration table. -2. The Camera Service obtains the **CameraHost** instance through the CameraDeviceHost service. The **CameraHost** instance can be used to obtain the bottom-layer camera capabilities, turn on the flashlight, call the **Open\(\)** interface to start the camera and create a connection, create a **DeviceManager** instance \(powering on the bottom-layer hardware modules\), and create a **CameraDevice** instance \(providing the device control interface for the upper layer\). When the **CameraDevice** instance is created, each submodule of PipelineCore is instantiated. Among the submodules, StreamPiplineCore is responsible for creating pipelines, and MetaQueueManager is responsible for reporting metadata. +2. The Camera Service obtains the **CameraHost** instance through the CameraDeviceHost service. The **CameraHost** instance can be used to obtain the bottom-layer camera capabilities, turn on the flashlight, call the **Open()** interface to start the camera and create a connection, create a **DeviceManager** instance (powering on the bottom-layer hardware modules), and create a **CameraDevice** instance (providing the device control interface for the upper layer). When the **CameraDevice** instance is created, each submodule of PipelineCore is instantiated. Among the submodules, StreamPipelineCore is responsible for creating pipelines, and MetaQueueManager is responsible for reporting metadata. -3. The Camera Service configures the stream and creates a **Stream** instance through the **CameraDevice** instance at the bottom layer. The StreamPipelineStrategy module creates the node connection mode of the corresponding stream by using the mode issued by the upper layer and querying the configuration table. The StreamPipelineBuilder module creates a node and returns the pipeline to the StreamPipelineDispatcher module through the connection. The StreamPipelineDispatcher module provides unified pipeline invoking management. +3. The Camera Service configures stream and creates a **Stream** class through the CameraDevice module. The StreamPipelineStrategy module creates the node connection mode of the corresponding stream by using the mode issued by the upper layer and querying the configuration table. The StreamPipelineBuilder module creates a node and returns the pipeline to the StreamPipelineDispatcher module through the connection. The StreamPipelineDispatcher module dispatches pipelines. -4. The Camera Service controls the stream operations through the **Stream** instance. The **AttachBufferQueue\(\)** interface is used to deliver the buffer queue requested from the display module to the bottom layer. The CameraDeviceDriverModel manages the buffer. After the **Capture\(\)** interface is called to deliver commands, the bottom layer transfers the buffer to the upper layer. The Image Signal Processor \(ISP\) node obtains a specified number of buffers from the buffer queue and delivers the buffers to the bottom-layer ISP hardware. After filling the buffers, the ISP hardware transfers the buffers to the CameraDeviceDriverModel. The CameraDeviceDriverModel fills the created pipeline with the received buffers by using a loop thread. Each node processes the pipeline data and transfers the data to the upper layer by using a callback. At the same time, the buffers are freed for reuse. +4. The Camera Service controls the stream operations through the **Stream** instance. The **AttachBufferQueue()** interface is used to deliver the buffer queue requested from the display module to the bottom layer. The CameraDeviceDriverModel manages the buffer. After the **Capture()** interface is called to deliver commands, the bottom layer transfers the buffer to the upper layer. The Image Signal Processor (ISP) node obtains a specified number of buffers from the buffer queue and delivers the buffers to the bottom-layer ISP hardware. After filling the buffers, the ISP hardware transfers the buffers to the CameraDeviceDriverModel. The CameraDeviceDriverModel fills the created pipeline with the received buffers by using a loop thread. Each node processes the pipeline data and transfers the data to the upper layer by using a callback. At the same time, the buffers are freed for reuse. -5. The Camera Service delivers the photographing command through the **Capture\(\)** interface. The **ChangeToOfflineStream\(\)** interface is used to query the position of the photographing buffer. If the ISP hardware has output an image and sent the image data to the IPP node, the common photographing streams can be converted into offline streams. Otherwise, the close process is executed. The **ChangeToOfflineStream\(\)** interface transfers **StreamInfo** to enable the offline stream to obtain the stream information of the common stream, confirms the node connection mode of the offline stream based on the configuration table, and creates the node connection of the offline stream. If the node connection has been created, the interface releases the node required by the non-offline stream through **CloseCamera\(\)**. It then waits for the buffer to return from the bottom-layer pipeline to the upper layer and then releases the pipeline resources. +5. The Camera Service delivers the photographing command through the **Capture()** interface. The **ChangeToOfflineStream()** interface is used to query the position of the photographing buffer. If the ISP hardware has output an image and sent the image data to the IPP node, the common photographing streams can be converted into offline streams. Otherwise, the close process is executed. The **ChangeToOfflineStream()** interface transfers **StreamInfo** to enable the offline stream to obtain the stream information of the common stream, confirms the node connection mode of the offline stream based on the configuration table, and creates the node connection of the offline stream. If the node connection has been created, the interface releases the node required by the non-offline stream through **CloseCamera()**. It then waits for the buffer to return from the bottom-layer pipeline to the upper layer and then releases the pipeline resources. -6. The Camera Service sends the **CaptureSetting** parameter to the CameraDeviceDriverModel through the **UpdateSettings\(\)** interface of the **CameraDevice** instance. The CameraDeviceDriverModel forwards the parameter to each node through the StreamPipelineDispatcher module. The **CaptureSetting** parameter carried in the **StartStreamingCapture\(\)** and **Capture\(\)** interfaces is forwarded to the node to which the stream belongs through the StreamPipelineDispatcher module. +6. The Camera Service sends the **CaptureSetting** parameter to the CameraDeviceDriverModel through the **UpdateSettings()** interface of the **CameraDevice** instance. The CameraDeviceDriverModel forwards the parameter to each node through the StreamPipelineDispatcher module. The **CaptureSetting** parameter carried in **StartStreamingCapture()** and **Capture()** is forwarded to the node to which the stream belongs through the StreamPipelineDispatcher module. -7. The Camera Service controls underlying metadata reporting through the **EnableResult\(\)** and **DisableResult\(\)** interfaces. If the bottom-layer metadata needs to be reported, the pipeline creates a buffer queue in the CameraDeviceDriverModel to collect and transfer metadata, queries the configuration table based on the StreamPipelineStrategy module, and creates and connects to the specified node through the StreamPipelineBuilder module. The MetaQueueManager module delivers the buffer to the bottom layer, and the bottom-layer node fills in data. The MetaQueueManager module then invokes the upper-layer callback to transfer the data to the upper layer. +7. The Camera Service controls underlying metadata reporting through the **EnableResult()** and **DisableResult()** interfaces. If the underlying metadata needs to be reported, the pipeline creates a buffer queue in the CameraDeviceDriverModel to collect and transfer metadata, queries the configuration table based on the StreamPipelineStrategy module, and creates and connects to the specified node through the StreamPipelineBuilder module. The MetaQueueManager module delivers the buffer to the bottom layer, and the bottom-layer node fills in data. The MetaQueueManager module then invokes the upper-layer callback to transfer the data to the upper layer. -8. The Camera Service calls the **Close\(\)** interface of the **CameraDevice** class, and the **CameraDevice** instance calls the corresponding DeviceManager module to power off each hardware. If an offline stream exists in the subpipeline of the IPP node, the offline stream must be reserved until the execution is complete. +8. The Camera Service calls the **Close()** interface of the **CameraDevice** class, and the **CameraDevice** instance calls the corresponding DeviceManager module to power off each hardware. If an offline stream exists in the subpipeline of the IPP node, the offline stream must be reserved until the execution is complete. -9. To implement dynamic frame control, a CollectBuffer thread is started in the StreamOperator. The CollectBuffer thread obtains a buffer from the buffer queue of each stream. If the frame rate of a stream needs to be controlled \(1/n of the sensor output frame rate\), the CollectBuffer thread can control the buffer packaging of each frame as required, and determine whether to collect the buffer of the stream. For example, if the output frame rate of the sensor is 120 fps and the preview stream frame rate is 30 fps, the CollectBuffer thread collects the buffer of the preview stream every 4 fps. +9. To implement dynamic frame control, a CollectBuffer thread is started in the StreamOperator. The CollectBuffer thread obtains a buffer from the buffer queue of each stream. If the frame rate of a stream needs to be controlled (1/n of the sensor output frame rate), the CollectBuffer thread can control the buffer packaging of each frame as required, and determine whether to collect the buffer of the stream. For example, if the output frame rate of the sensor is 120 fps and the preview stream frame rate is 30 fps, the CollectBuffer thread collects the buffer of the preview stream every 4 fps. + -## Development Guidelines +## Development Guidelines -### Available APIs -For details about the HDI functionalities and the function passing rules, see "Available APIs" in [Camera](https://gitee.com/openharmony/drivers_peripheral/blob/master/camera/README_zh.md). +### When to Use -### How to Develop +The camera module encapsulates camera operations in camera preview, photographing, and video streams to facilitate camera hardware operations and improve development efficiency. -The following describes the main APIs of the camera driver model, including the APIs for registering and detecting cameras, creating, capturing, and destroying streams, and enabling and disabling devices. \(To clearly describe the implementation of main functionalities, some error judgment and log source code are not described here.\) +### Available APIs -1. Register a **CameraHost**. +- icamera_device.h - Define the **HdfDriverEntry** structure to define the method for initializing a **CameraHost**. + | API | Description | + | ------------------------------------------------------------ | ---------------------------- | + | CamRetCode GetStreamOperator(
const OHOS::sptr &callback,
OHOS::sptr &streamOperator) | Obtains the stream controller. | + | CamRetCode UpdateSettings(const std::shared_ptr &settingss) | Updates device control parameters. | + | CamRetCode SetResultMode(const ResultCallbackMode &mode) | Sets the result callback mode and function.| + | CamRetCode GetEnabledResults(std::vector &results) | Obtains the enabled ResultMeta. | + | CamRetCode EnableResult(const std::vector &results) | Enables specific ResultMeta. | + | CamRetCode DisableResult(const std::vector &results) | Disables specific ResultMeta. | + | void Close() | Closes the camera device. | - ``` - struct HdfDriverEntry g_cameraHostDriverEntry = { - .moduleVersion = 1, - .moduleName = "camera_service", - .Bind = HdfCameraHostDriverBind, - .Init = HdfCameraHostDriverInit, - .Release = HdfCameraHostDriverRelease, - }; - HDF_INIT(g_cameraHostDriverEntry); // Register the HdfDriverEntry structure with the HDF. - ``` +- icamera_device_callback.h -2. Initialize the **CameraHost**. + | API | Description | + | ------------------------------------------------------------ | ------------------------------------------------------------ | + | void OnError(ErrorType type, int32_t errorCode) | Called when an error occurs on the device to return error information. You need to implement this interface.| + | void OnResult(uint64_t timestamp, const std::shared_ptr &result) | Callback invoked to report metadata related to the camera device. | - **HdfCameraHostDriverBind** defined in the **HdfDriverEntry** structure provides the registration of **CameraServiceDispatch\(\)** and **CameraHostStubInstance\(\)**. **CameraServiceDispatch\(\)** is used to remotely call a method of the **CameraHost**, such as **OpenCamera\(\)** and **SetFlashlight\(\)**. **CameraHostStubInstance\(\)** is used to initialize the camera device, which is called during system startup. - ``` - int HdfCameraHostDriverBind(HdfDeviceObject *deviceObject) - { - HDF_LOGI("HdfCameraHostDriverBind enter!"); - if (deviceObject == nullptr) { - HDF_LOGE("HdfCameraHostDriverBind: HdfDeviceObject is NULL !"); - return HDF_FAILURE; - } - HdfCameraService *hdfCameraService = reinterpret_cast(OsalMemAlloc(sizeof(HdfCameraService))); - if (hdfCameraService == nullptr) { - HDF_LOGE("HdfCameraHostDriverBind OsalMemAlloc HdfCameraService failed!"); - return HDF_FAILURE; - } - hdfCameraService->ioservice.Dispatch = CameraServiceDispatch; // Used to call methods of the CameraHost. - hdfCameraService->ioservice.Open = nullptr; - hdfCameraService->ioservice.Release = nullptr; - hdfCameraService->instance = CameraHostStubInstance(); // Initialize the camera device. - deviceObject->service = &hdfCameraService->ioservice; - return HDF_SUCCESS; - } - ``` +- icamera_host.h - The following functions are the implementation of the methods of the **CameraHost**: + | API | Description | + | ------------------------------------------------------------ | ------------------------------ | + | CamRetCode SetCallback(const OHOS::sptr &callback) | Sets the **ICameraHostCallback** API. | + | CamRetCode GetCameraIds(std::vector\ &cameraIds) | Obtains the IDs of available camera devices.| + | CamRetCode GetCameraAbility(const std::string &cameraId,
std::shared_ptr &ability) | Obtains the abilities of a camera device. | + | CamRetCode OpenCamera(const std::string &cameraId,
const OHOS::sptr &callback,
OHOS::sptr &device) | Opens a camera. | + | CamRetCode SetFlashlight(const std::string &cameraId, bool &isEnable) | Turns on or off the flash. | - ``` - int32_t CameraHostStub::CameraHostServiceStubOnRemoteRequest(int cmdId, MessageParcel &data, - MessageParcel &reply, MessageOption &option) - { - switch(cmdId) { - case CMD_CAMERA_HOST_SET_CALLBACK: { - return CameraHostStubSetCallback(data, reply, option); - } - case CMD_CAMERA_HOST_GET_CAMERAID: { - return CameraHostStubGetCameraIds(data, reply, option); - } - case CMD_CAMERA_HOST_GET_CAMERA_ABILITY: { - return CameraHostStubGetCameraAbility(data, reply, option); - } - case CMD_CAMERA_HOST_OPEN_CAMERA: { - return CameraHostStubOpenCamera(data, reply, option); - } - case CMD_CAMERA_HOST_SET_FLASH_LIGHT: { - return CameraHostStubSetFlashlight(data, reply, option); - } - default: { - HDF_LOGE("%s: not support cmd %d", __func__, cmdId); - return HDF_ERR_INVALID_PARAM; - } - } - return HDF_SUCCESS; - } - ``` +- icamera_host_callback.h - **CameraHostStubInstance\(\)** finally calls **CameraHostImpl::Init\(\)** to obtain the physical camera and initialize the DeviceManager and PipelineCore modules. + | API | Description | + | ------------------------------------------------------------ | ---------------------- | + | void OnCameraStatus(const std::string &cameraId, CameraStatus status) | Reports camera status changes.| + | void OnFlashlightStatus(const std::string &cameraId, FlashlightStatus status) | Callback invoked to report the flash status changes. | -3. Obtain the **CamerHost**. +- ioffline_stream_operator.h - Call the **Get\(\)** interface to obtain the **CameraHost** from the **CameraService**. The **Get\(\)** interface is as follows: + | API | Description | + | ------------------------------------------------------------ | -------------- | + | CamRetCode CancelCapture(int captureId) | Cancels a capture request. | + | CamRetCode ReleaseStreams(const std::vector &streamIds) | Releases streams. | + | CamRetCode Release() | Releases all offline streams.| - ``` - sptr ICameraHost::Get(const char *serviceName) - { - do { - using namespace OHOS::HDI::ServiceManager::V1_0; - auto servMgr = IServiceManager::Get(); - if (servMgr == nullptr) { - HDF_LOGE("%s: IServiceManager failed!", __func__); - break; - } - auto remote = servMgr->GetService(serviceName); // Obtain the CameraHost based on serviceName. - if (remote != nullptr) { - sptr hostSptr = iface_cast(remote); // Return the CameraHostProxy object that contains methods such as OpenCamera() to the caller. - return hostSptr; - } - HDF_LOGE("%s: GetService failed! serviceName = %s", __func__, serviceName); - } while(false); - HDF_LOGE("%s: get %s failed!", __func__, serviceName); - return nullptr; - } - ``` +- istream_operator.h -4. Implement the **OpenCamera\(\)** interface. + | API | Description | + | ------------------------------------------------------------ | -------------------------------- | + | CamRetCode IsStreamsSupported(
OperationMode mode,
const std::shared_ptr\ &modeSetting,
const std::vector<std::shared_ptr<StreamInfo>> &info,
StreamSupportType &type) | Checks whether a stream can be added. | + | CamRetCode CreateStreams(const std::vector> &streamInfos) | Creates streams. | + | CamRetCode ReleaseStreams(const std::vector &streamIds) | Releases streams. | + | CamRetCode CommitStreams(OperationMode mode,
const std::shared_ptr &modeSetting) | Configure streams. | + | CamRetCode GetStreamAttributes(
std::vector> &attributes) | Obtain stream attributes. | + | CamRetCode AttachBufferQueue(int streamId, const OHOS::sptr\ &producer) | Attaches a producer handle to a stream. | + | CamRetCode DetachBufferQueue(int streamId) | Detaches a producer handle from a stream.| + | CamRetCode Capture(int captureId,
const std::shared_ptr &info, bool isStreaming) | Captures images. | + | CamRetCode CancelCapture(int captureId) | Cancels a capture. | + | CamRetCode ChangeToOfflineStream(const std::vector &streamIds,
OHOS::sptr &callback,
OHOS::sptr &offlineOperator) | Changes a stream into an offline stream. | - The **CameraHostProxy** class provides five interfaces: **SetCallback\(\)**, **GetCameraIds\(\)**, **GetCameraAbility\(\)**, **OpenCamera\(\)**, and **SetFlashlight\(\)**. The following describes **OpenCamera\(\)**. +- istream_operator_callback.h - The **OpenCamera\(\)** interface calls the remote **CameraHostStubOpenCamera\(\)** interface through the CMD\_CAMERA\_HOST\_OPEN\_CAMERA to obtain an **ICameraDevice** object. + | API | Description | + | ------------------------------------------------------------ | ---------------------------------------- | + | void OnCaptureStarted(int32_t captureId, const std::vector &streamIds) | Called when a capture starts. | + | void OnCaptureEnded(int32_t captureId,
const std::vector> &infos) | Called when a capture ends. | + | void OnCaptureError(int32_t captureId,
const std::vector> &infos) | Called when an error occurs during the capture.| + | void OnFrameShutter(int32_t captureId,
const std::vector &streamIds, uint64_t timestamp) | Called when a frame is captured. | - ``` - CamRetCode CameraHostProxy::OpenCamera(const std::string &cameraId, const OHOS::sptr &callback, OHOS::sptr &pDevice) - { - int32_t ret = Remote()->SendRequest(CMD_CAMERA_HOST_REMOTE_OPEN_CAMERA, data, reply, option); - if (ret != HDF_SUCCESS) { - HDF_LOGE("%{public}s: SendRequest failed, error code is %{public}d", __func__, ret); - return INVALID_ARGUMENT; - } - CamRetCode retCode = static_cast(reply.ReadInt32()); - bool flag = reply.ReadBool(); - if (flag) { - sptr remoteCameraDevice = reply.ReadRemoteObject(); - if (remoteCameraDevice == nullptr) { - HDF_LOGE("%{public}s: CameraHostProxy remoteCameraDevice is null", __func__); - } - pDevice = OHOS::iface_cast(remoteCameraDevice); - } - return retCode; - } - ``` +### How to Develop +To camera driver development procedure is as follows: - **Remote\(\)-\>SendRequest** calls **CameraHostServiceStubOnRemoteRequest\(\)**, enters the **CameraHostStubOpenCamera\(\)** interface based on **cmdId**, and finally calls **CameraHostImpl::OpenCamera\(\)** to obtain a **CameraDevice** and power on the camera hardware. +1. Register a **CameraHost**. + Define the **HdfDriverEntry** structure to define the method for initializing a **CameraHost**. ``` - CamRetCode CameraHostImpl::OpenCamera(const std::string &cameraId, const OHOS::sptr &callback, OHOS::sptr &device) - { - std::shared_ptr cameraDevice = std::static_pointer_cast(itr->second); - if (cameraDevice == nullptr) { - CAMERA_LOGE("camera device is null."); - return INSUFFICIENT_RESOURCES; - } - CamRetCode ret = cameraDevice->SetCallback(callback); - if (ret != NO_ERROR) { - CAMERA_LOGW("set camera device callback faild."); - return ret; - } - CameraHostConfig *config = CameraHostConfig::GetInstance(); - if (config == nullptr) { - return INVALID_ARGUMENT; - } - std::vector phyCameraIds; - RetCode rc = config->GetPhysicCameraIds(cameraId, phyCameraIds); - if (rc != RC_OK) { - CAMERA_LOGE("get physic cameraId failed."); - return DEVICE_ERROR; - } - if (CameraPowerUp(cameraId, phyCameraIds) != RC_OK) { // Power on the camera hardware. - CAMERA_LOGE("camera powerup failed."); - CameraPowerDown(phyCameraIds); - return DEVICE_ERROR; - } - - auto sptrDevice = deviceBackup_.find(cameraId); - if (sptrDevice == deviceBackup_.end()) { - deviceBackup_[cameraId] = cameraDevice.get(); - } - device = deviceBackup_[cameraId]; - cameraDevice->SetStatus(true); - return NO_ERROR; - } - ``` + struct HdfDriverEntry g_cameraHostDriverEntry = { + .moduleVersion = 1, + .moduleName = "camera_service", + .Bind = HdfCameraHostDriverBind, + .Init = HdfCameraHostDriverInit, + .Release = HdfCameraHostDriverRelease, + }; + HDF_INIT(g_cameraHostDriverEntry); // Register the HdfDriverEntry structure with the HDF. + ``` -5. Implement the **GetStreamOperator\(\)** interface. - - **CameraDeviceImpl** defines interfaces such as **GetStreamOperator\(\)**, **UpdateSettings\(\)**, **SetResultMode\(\)**, and **GetEnabledResult\(\)**. The following is an example of implementing the **GetStreamOperator\(\)** interface: - - ``` - CamRetCode CameraDeviceImpl::GetStreamOperator(const OHOS::sptr &callback, - OHOS::sptr &streamOperator) - { - if (callback == nullptr) { - CAMERA_LOGW("input callback is null."); - return INVALID_ARGUMENT; - } - spCameraDeciceCallback_ = callback; - if (spStreamOperator_ == nullptr) { - // Here, an spStreamOperator object is created and passed to the caller for stream operations. - spStreamOperator_ = new(std::nothrow) StreamOperatorImpl(spCameraDeciceCallback_, shared_from_this()); - if (spStreamOperator_ == nullptr) { - CAMERA_LOGW("create stream operator failed."); - return DEVICE_ERROR; - } - ismOperator_ = spStreamOperator_; - } - streamOperator = ismOperator_; - - spStreamOperator_->SetRequestCallback([this](){ - cameraDeciceCallback_->OnError(REQUEST_TIMEOUT, 0); - }); - } - ``` - -6. Create a stream. +2. Initialize the **CameraHost**. - Fill in the **StreamInfo** structure before creating a stream by calling **CreateStreams\(\)**. + **HdfCameraHostDriverBind** defined in the **HdfDriverEntry** structure provides the registration of **CameraServiceDispatch()** and **CameraHostStubInstance()**. **CameraServiceDispatch()** is used to remotely call a method of the **CameraHost**, such as **OpenCamera()** and **SetFlashlight()**. **CameraHostStubInstance()** is used to initialize the camera device, which is called during system startup. + + ``` + int HdfCameraHostDriverBind(HdfDeviceObject *deviceObject) + { + HDF_LOGI("HdfCameraHostDriverBind enter!"); + if (deviceObject == nullptr) { + HDF_LOGE("HdfCameraHostDriverBind: HdfDeviceObject is NULL !"); + return HDF_FAILURE; + } + HdfCameraService *hdfCameraService = reinterpret_cast(OsalMemAlloc(sizeof(HdfCameraService))); + if (hdfCameraService == nullptr) { + HDF_LOGE("HdfCameraHostDriverBind OsalMemAlloc HdfCameraService failed!"); + return HDF_FAILURE; + } + hdfCameraService->ioservice.Dispatch = CameraServiceDispatch; // Used to call methods of the CameraHost. + hdfCameraService->ioservice.Open = nullptr; + hdfCameraService->ioservice.Release = nullptr; + hdfCameraService->instance = CameraHostStubInstance(); // Initialize the camera device. + deviceObject->service = &hdfCameraService->ioservice; + return HDF_SUCCESS; + } + ``` + + The following functions are the implementation of the methods of the **CameraHost**: + + ``` + int32_t CameraHostStub::CameraHostServiceStubOnRemoteRequest(int cmdId, MessageParcel &data, + MessageParcel &reply, MessageOption &option) + { + switch(cmdId) { + case CMD_CAMERA_HOST_SET_CALLBACK: { + return CameraHostStubSetCallback(data, reply, option); + } + case CMD_CAMERA_HOST_GET_CAMERAID: { + return CameraHostStubGetCameraIds(data, reply, option); + } + case CMD_CAMERA_HOST_GET_CAMERA_ABILITY: { + return CameraHostStubGetCameraAbility(data, reply, option); + } + case CMD_CAMERA_HOST_OPEN_CAMERA: { + return CameraHostStubOpenCamera(data, reply, option); + } + case CMD_CAMERA_HOST_SET_FLASH_LIGHT: { + return CameraHostStubSetFlashlight(data, reply, option); + } + default: { + HDF_LOGE("%s: not support cmd %d", __func__, cmdId); + return HDF_ERR_INVALID_PARAM; + } + } + return HDF_SUCCESS; + } + ``` + + **CameraHostStubInstance()** finally calls **CameraHostImpl::Init()** to obtain the physical camera and initialize the DeviceManager and PipelineCore modules. + +3. Obtain the **CameraHost**. + + Call the **Get()** interface to obtain the **CameraHost** from the **CameraService**. The **Get()** interface is as follows: + + ``` + sptr ICameraHost::Get(const char *serviceName) + { + do { + using namespace OHOS::HDI::ServiceManager::V1_0; + auto servMgr = IServiceManager::Get(); + if (servMgr == nullptr) { + HDF_LOGE("%s: IServiceManager failed!", __func__); + break; + } + auto remote = servMgr->GetService(serviceName); // Obtain the CameraHost based on serviceName. + if (remote != nullptr) { + sptr hostSptr = iface_cast(remote); // Return the CameraHostProxy object that contains interfaces such as OpenCamera() to the caller. + return hostSptr; + } + HDF_LOGE("%s: GetService failed! serviceName = %s", __func__, serviceName); + } while(false); + HDF_LOGE("%s: get %s failed!", __func__, serviceName); + return nullptr; + } + ``` - ``` - using StreamInfo = struct _StreamInfo { - int streamId_; - int width_; // Stream width - int height_; // Stream height - int format_; // Stream format, for example, PIXEL_FMT_YCRCB_420_SP - int datasapce_; - StreamIntent intent_; // StreamIntent, for example, PREVIEW - bool tunneledMode_; - OHOS::sptr bufferQueue_; // The stream buffer queue can be created by using the streamCustomer->CreateProducer() interface. - int minFrameDuration_; - EncodeType encodeType_; - }; - ``` +4. Implement the **OpenCamera\(\)** interface. - The **CreateStreams\(\)** interface in the **StreamOperatorImpl** class is used to create a **StreamBase** instance, which can then be used to initialize operations such as **CreateBufferPool\(\)** by using the **init\(\)** method. + The **CameraHostProxy** class provides five interfaces: **SetCallback()**, **GetCameraIds()**, **GetCameraAbility()**, **OpenCamera()**, and **SetFlashlight()**. The following describes **OpenCamera()**. + The **OpenCamera()** interface calls the remote **CameraHostStubOpenCamera()** interface through the CMD_CAMERA_HOST_OPEN_CAMERA to obtain an **ICameraDevice** object. + + ``` + CamRetCode CameraHostProxy::OpenCamera(const std::string &cameraId, const OHOS::sptr &callback, OHOS::sptr &pDevice) + { + int32_t ret = Remote()->SendRequest(CMD_CAMERA_HOST_REMOTE_OPEN_CAMERA, data, reply, option); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%{public}s: SendRequest failed, error code is %{public}d", __func__, ret); + return INVALID_ARGUMENT; + } + CamRetCode retCode = static_cast(reply.ReadInt32()); + bool flag = reply.ReadBool(); + if (flag) { + sptr remoteCameraDevice = reply.ReadRemoteObject(); + if (remoteCameraDevice == nullptr) { + HDF_LOGE("%{public}s: CameraHostProxy remoteCameraDevice is null", __func__); + } + pDevice = OHOS::iface_cast(remoteCameraDevice); + } + return retCode; + } + ``` + + **Remote()->SendRequest** calls **CameraHostServiceStubOnRemoteRequest()**, enters the **CameraHostStubOpenCamera()** interface based on **cmdId**, and finally calls **CameraHostImpl::OpenCamera()** to obtain a **CameraDevice** and power on the camera hardware. + + ``` + CamRetCode CameraHostImpl::OpenCamera(const std::string &cameraId, const OHOS::sptr &callback, OHOS::sptr &device) + { + std::shared_ptr cameraDevice = std::static_pointer_cast(itr->second); + if (cameraDevice == nullptr) { + CAMERA_LOGE("camera device is null."); + return INSUFFICIENT_RESOURCES; + } + CamRetCode ret = cameraDevice->SetCallback(callback); + if (ret != NO_ERROR) { + CAMERA_LOGW("set camera device callback failed."); + return ret; + } + CameraHostConfig *config = CameraHostConfig::GetInstance(); + if (config == nullptr) { + return INVALID_ARGUMENT; + } + std::vector phyCameraIds; + RetCode rc = config->GetPhysicCameraIds(cameraId, phyCameraIds); + if (rc != RC_OK) { + CAMERA_LOGE("get physic cameraId failed."); + return DEVICE_ERROR; + } + if (CameraPowerUp(cameraId, phyCameraIds) != RC_OK) { // Power on the camera hardware. + CAMERA_LOGE("camera powerup failed."); + CameraPowerDown(phyCameraIds); + return DEVICE_ERROR; + } + + auto sptrDevice = deviceBackup_.find(cameraId); + if (sptrDevice == deviceBackup_.end()) { + deviceBackup_[cameraId] = cameraDevice.get(); + } + device = deviceBackup_[cameraId]; + cameraDevice->SetStatus(true); + return NO_ERROR; + } + ``` - ``` - RetCode StreamOperatorImpl::CreateStream(const std::shared_ptr& streamInfo) - { - static std::map typeMap = { - {PREVIEW, "PREVIEW"}, - {VIDEO, "VIDEO"}, - {STILL_CAPTURE, "STILL_CAPTURE"}, - {POST_VIEW, "POST_VIEW"}, {ANALYZE, "ANALYZE"}, - {CUSTOM, "CUSTOM"} - }; - - auto itr = typeMap.find(streamInfo->intent_); - if (itr == typeMap.end()) { - CAMERA_LOGE("do not support stream type. [type = %{public}d]", streamInfo->intent_); - return RC_ERROR; - } - std::shared_ptr stream = StreamFactory::Instance().CreateShared(itr->second); // Create a StreamBase instance. - RetCode rc = stream->Init(streamInfo); - return RC_OK; - } - ``` +5. Implement the **GetStreamOperator\(\)** interface. -7. Configure the stream. + **CameraDeviceImpl** defines interfaces such as **GetStreamOperator()**, **UpdateSettings()**, **SetResultMode()**, and **GetEnabledResult()**. The following is an example of implementing the **GetStreamOperator()** interface: + + ``` + CamRetCode CameraDeviceImpl::GetStreamOperator(const OHOS::sptr &callback, + OHOS::sptr &streamOperator) + { + if (callback == nullptr) { + CAMERA_LOGW("input callback is null."); + return INVALID_ARGUMENT; + } + spCameraDeviceCallback_ = callback; + if (spStreamOperator_ == nullptr) { + // Here, an spStreamOperator object is created and passed to the caller for stream operations. + spStreamOperator_ = new(std::nothrow) StreamOperatorImpl(spCameraDeviceCallback_, shared_from_this()); + if (spStreamOperator_ == nullptr) { + CAMERA_LOGW("create stream operator failed."); + return DEVICE_ERROR; + } + ismOperator_ = spStreamOperator_; + } + streamOperator = ismOperator_; + + spStreamOperator_->SetRequestCallback([this](){ + spCameraDeviceCallback_->OnError(REQUEST_TIMEOUT, 0); + }); + } + ``` - Use the **CommitStreams\(\)** method to configure the stream, including PipelineCore initialization and creation. It must be called after the stream is created. +6. Create a stream. - ``` - CamRetCode StreamOperatorImpl::CommitStreams(OperationMode mode, const std::shared_ptr& modeSetting) - { - auto cameraDevice = cameraDevice_.lock(); - if (cameraDevice == nullptr) { - CAMERA_LOGE("camera device closed."); - return CAMERA_CLOSED; - } - std::shared_ptr PipelineCore = - std::static_pointer_cast(cameraDevice)->GetPipelineCore(); - if (PipelineCore == nullptr) { + Fill in the **StreamInfo** structure before creating a stream by calling **CreateStreams()**. + + ``` + using StreamInfo = struct _StreamInfo { + int streamId_; + int width_; // Stream width + int height_; // Stream height + int format_; // Stream format, for example, PIXEL_FMT_YCRCB_420_SP + int dataSpace_; + StreamIntent intent_; // StreamIntent, for example, PREVIEW + bool tunneledMode_; + OHOS::sptr bufferQueue_; // The stream buffer queue can be created by using the streamCustomer->CreateProducer() interface. + int minFrameDuration_; + EncodeType encodeType_; + }; + ``` + + The **CreateStreams()** interface in the **StreamOperatorImpl** class is used to create a **StreamBase** instance, which can then be used to initialize operations such as **CreateBufferPool()** by using the **init()** interface. + + ``` + RetCode StreamOperatorImpl::CreateStream(const std::shared_ptr& streamInfo) + { + static std::map typeMap = { + {PREVIEW, "PREVIEW"}, + {VIDEO, "VIDEO"}, + {STILL_CAPTURE, "STILL_CAPTURE"}, + {POST_VIEW, "POST_VIEW"}, {ANALYZE, "ANALYZE"}, + {CUSTOM, "CUSTOM"} + }; + + auto itr = typeMap.find(streamInfo->intent_); + if (itr == typeMap.end()) { + CAMERA_LOGE("do not support stream type. [type = %{public}d]", streamInfo->intent_); + return RC_ERROR; + } + std::shared_ptr stream = StreamFactory::Instance().CreateShared(itr->second); // Create a StreamBase instance. + RetCode rc = stream->Init(streamInfo); + return RC_OK; + } + ``` + +7. Configure the stream. + + Use the **CommitStreams()** interface to configure the stream, including PipelineCore initialization and creation. It must be called after the stream is created. + + ``` + CamRetCode StreamOperatorImpl::CommitStreams(OperationMode mode, const std::shared_ptr& modeSetting) + { + auto cameraDevice = cameraDevice_.lock(); + if (cameraDevice == nullptr) { + CAMERA_LOGE("camera device closed."); + return CAMERA_CLOSED; + } + std::shared_ptr PipelineCore = + std::static_pointer_cast(cameraDevice)->GetPipelineCore(); + if (PipelineCore == nullptr) { CAMERA_LOGE("Failed to obtain PipelineCore."); - return CAMERA_CLOSED; - } - - streamPipeCore_ = PipelineCore->GetStreamPipelineCore(); - if (streamPipeCore_ == nullptr) { + return CAMERA_CLOSED; + } + + streamPipeCore_ = PipelineCore->GetStreamPipelineCore(); + if (streamPipeCore_ == nullptr) { CAMERA_LOGE("Failed to obtain the stream PipelineCore."); - return DEVICE_ERROR; - } - - RetCode rc = streamPipeCore_->Init(); // Initialize the PipelineCore. - if (rc != RC_OK) { + return DEVICE_ERROR; + } + + RetCode rc = streamPipeCore_->Init(); // Initialize the PipelineCore. + if (rc != RC_OK) { CAMERA_LOGE("Failed to initialize the stream PipelineCore."); - return DEVICE_ERROR; - } - rc = streamPipeCore_->CreatePipeline(mode); // Create a pipeline. - if (rc != RC_OK) { + return DEVICE_ERROR; + } + rc = streamPipeCore_->CreatePipeline(mode); // Create a pipeline. + if (rc != RC_OK) { CAMERA_LOGE("Failed to create pipeline."); - return INVALID_ARGUMENT; - } - return NO_ERROR; - } - ``` - -8. Capture images. - - Fill in the **CaptureInfo** structure before calling the **Capture\(\)** method. - - ``` - using CaptureInfo = struct _CaptureInfo { - std::vector streamIds_; // IDs of streams to be captured - std::shared_ptr captureSetting_; // Camera ability can be obtained through the GetCameraAbility() interface of CameraHost. - bool enableShutterCallback_; - }; - ``` - - Use the **Capture\(\)** interface in **StreamOperatorImpl** to call the **CreateCapture\(\)** interface to capture streams. - - ``` - CamRetCode StreamOperatorImpl::Capture(int captureId, const std::shared_ptr& captureInfo, bool isStreaming) - { - if (!ValidCaptureInfo(captureId, captureInfo)) { - CAMERA_LOGE("capture streamIds is empty. [captureId = %d]", captureId); - return INVALID_ARGUMENT; - } - std::shared_ptr cameraCapture = nullptr; - RetCode rc = CreateCapture(captureId, captureInfo, isStreaming, cameraCapture); - if (rc != RC_OK) { - CAMERA_LOGE("create capture is failed."); - return DEVICE_ERROR; - } - - { - std::unique_lock lock(captureMutex_); - camerCaptureMap_.insert(std::make_pair(captureId, cameraCapture)); - } - - rc = StartThread(); - if (rc != RC_OK) { - CAMERA_LOGE("preview start failed."); - return DEVICE_ERROR; - } - return NO_ERROR; - } - ``` - -9. Cancel the capture and release the offline stream. - - Use the **CancelCapture\(\)** interface in the **StreamOperatorImpl** class to cancel the stream capture based on **captureId**. - - ``` - CamRetCode StreamOperatorImpl::CancelCapture(int captureId) - { - auto itr = camerCaptureMap_.find(captureId); // Search for the CameraCapture object in the Map based on the captureId. - RetCode rc = itr->second->Cancel(); // Call the Cancel() interface in CameraCapture to cancel the stream capture. - std::unique_lock lock(captureMutex_); - camerCaptureMap_.erase(itr); // Erase the CameraCapture object. - return NO_ERROR; - } - ``` - - Use the **ReleaseStreams\(\)** interface in the **StreamOperatorImpl** class t release the streams created by using **CreateStream\(\)** and **CommitStreams\(\)** and destroy the pipeline. - - ``` - CamRetCode StreamOperatorImpl::ReleaseStreams(const std::vector& streamIds) - { - RetCode rc = DestroyStreamPipeline(streamIds); // Destroy the pipeline based on streamIds. - rc = DestroyHostStreamMgr(streamIds); - rc = DestroyStreams(streamIds); // Destroy the stream specified by streamIds. - return NO_ERROR; - } - ``` + return INVALID_ARGUMENT; + } + return NO_ERROR; + } + ``` + +8. Capture images. + + Fill in the **CaptureInfo** structure before calling the **Capture()** method. + + ``` + using CaptureInfo = struct _CaptureInfo { + std::vector streamIds_; // IDs of streams to be captured + std::shared_ptr captureSetting_; // Camera ability can be obtained through the GetCameraAbility() interface of CameraHost. + bool enableShutterCallback_; + }; + ``` + + Use the **Capture()** interface in **StreamOperatorImpl** to call the **CreateCapture()** interface to capture streams. + + ``` + CamRetCode StreamOperatorImpl::Capture(int captureId, const std::shared_ptr& captureInfo, bool isStreaming) + { + if (!ValidCaptureInfo(captureId, captureInfo)) { + CAMERA_LOGE("capture streamIds is empty. [captureId = %d]", captureId); + return INVALID_ARGUMENT; + } + std::shared_ptr cameraCapture = nullptr; + RetCode rc = CreateCapture(captureId, captureInfo, isStreaming, cameraCapture); + if (rc != RC_OK) { + CAMERA_LOGE("create capture is failed."); + return DEVICE_ERROR; + } + + { + std::unique_lock lock(captureMutex_); + camerCaptureMap_.insert(std::make_pair(captureId, cameraCapture)); + } + + rc = StartThread(); + if (rc != RC_OK) { + CAMERA_LOGE("preview start failed."); + return DEVICE_ERROR; + } + return NO_ERROR; + } + ``` + +9. Cancel the capture and release the offline stream. + + Use the **CancelCapture()** interface in the **StreamOperatorImpl** class to cancel the stream capture based on **captureId**. + + ``` + CamRetCode StreamOperatorImpl::CancelCapture(int captureId) + { + auto itr = camerCaptureMap_.find(captureId); // Search for the CameraCapture object in the Map based on the captureId. + RetCode rc = itr->second->Cancel(); // Call the Cancel() interface in CameraCapture to cancel the stream capture. + std::unique_lock lock(captureMutex_); + camerCaptureMap_.erase(itr); // Erase the CameraCapture object. + return NO_ERROR; + } + ``` + + Use the **ReleaseStreams()** interface in the **StreamOperatorImpl** class t release the streams created by using **CreateStream()** and **CommitStreams()** and destroy the pipeline. + + ``` + CamRetCode StreamOperatorImpl::ReleaseStreams(const std::vector& streamIds) + { + RetCode rc = DestroyStreamPipeline(streamIds); // Destroy the pipeline based on streamIds. + rc = DestroyHostStreamMgr(streamIds); + rc = DestroyStreams(streamIds); // Destroy the stream specified by streamIds. + return NO_ERROR; + } + ``` 10. Close the camera device. - - Use the **Close\(\)** interface in the **CameraDeviceImpl** class to close the camera device. This interface calls **PowerDown\(\)** in the **DeviceManager** to power off the device. - - -## Development Example - -There is a camera demo in the **/drivers/peripheral/camera/hal/init** directory. After system startup, the executable file **ohos\_camera\_demo** is generated in the **/system/bin** directory. This demo can implement basic camera capabilities such as preview and photographing. The following uses the demo as an example to describe how to use the HDI to implement the **PreviewOn\(\)** and **CaptureON\(\)** interfaces. - -1. Construct a Hos3516Demo object in the **main** function. This object contains methods for initializing the camera and starting, stopping, and releasing streams. The **mainDemo-\>InitSensors\(\)** function is used to initialize the **CameraHost**, and the **mainDemo-\>InitCameraDevice\(\)** function is used to initialize the **CameraDevice**. - - ``` - int main(int argc, char** argv) - { - RetCode rc = RC_OK; - auto mainDemo = std::make_shared(); - rc = mainDemo->InitSensors(); // Initialize the CameraHost. - if (rc == RC_ERROR) { - CAMERA_LOGE("main test: mainDemo->InitSensors() error\n"); - return -1; - } - - rc = mainDemo->InitCameraDevice(); // Initialize the CameraDevice. - if (rc == RC_ERROR) { - CAMERA_LOGE("main test: mainDemo->InitCameraDevice() error\n"); - return -1; - } - - rc = PreviewOn(0, mainDemo); // Configure and enable streams. - if (rc != RC_OK) { - CAMERA_LOGE("main test: PreviewOn() error demo exit"); - return -1; - } - - ManuList(mainDemo, argc, argv); // Print the menu to the console. - - return RC_OK; - } - ``` - - The function used to initialize the **CameraHost** is implemented as follows, where the HDI **ICameraHost::Get\(\)** is called to obtain the **demoCameraHost** and set the callback: - - ``` - RetCode Hos3516Demo::InitSensors() - { - demoCameraHost_ = ICameraHost::Get(DEMO_SERVICE_NAME); - if (demoCameraHost_ == nullptr) { - CAMERA_LOGE("demo test: ICameraHost::Get error"); - return RC_ERROR; - } - - hostCallback_ = new CameraHostCallback(); - rc = demoCameraHost_->SetCallback(hostCallback_); - return RC_OK; - } - ``` - - The implementation of the function for initializing the **CameraDevice** is as follows, where the **GetCameraIds\(cameraIds\_\)**, **GetCameraAbility\(cameraId, ability\_\)**, and **OpenCamera\(cameraIds\_.front\(\), callback, demoCameraDevice\_\)** interfaces are called to obtain the **demoCameraHost**. - - ``` - RetCode Hos3516Demo::InitCameraDevice() - { - (void)demoCameraHost_->GetCameraIds(cameraIds_); - const std::string cameraId = cameraIds_.front(); - demoCameraHost_->GetCameraAbility(cameraId, ability_); - - sptr callback = new CameraDeviceCallback(); - rc = demoCameraHost_->OpenCamera(cameraIds_.front(), callback, demoCameraDevice_); - return RC_OK; - } - ``` - -2. Implement the **PreviewOn\(\)** interface to configure streams, enable preview streams, and start stream capture. After this interface is called, the camera preview channel starts running. Two streams are enabled: preview stream and capture or video stream. Only the preview stream will be captured. - - ``` - static RetCode PreviewOn(int mode, const std::shared_ptr& mainDemo) - { - rc = mainDemo->StartPreviewStream(); // Configure the preview stream. - if (mode == 0) { - rc = mainDemo->StartCaptureStream(); // Configure the capture stream. - } else { - rc = mainDemo->StartVideoStream(); // Configure the video stream. - } - - rc = mainDemo->CaptureON(STREAM_ID_PREVIEW, CAPTURE_ID_PREVIEW, CAPTURE_PREVIEW); // Capture the preview stream. - return RC_OK; - } - ``` - - The **StartCaptureStream\(\)**, **StartVideoStream\(\)**, and **StartPreviewStream\(\)** interfaces call the **CreateStream\(\)** interface with different input parameters. - - ``` - RetCode Hos3516Demo::StartVideoStream() - { - RetCode rc = RC_OK; - if (isVideoOn_ == 0) { - isVideoOn_ = 1; - rc = CreateStream(STREAM_ID_VIDEO, streamCustomerVideo_, VIDEO); // To enable the preview stream or capture stream, change the input parameters. - } - return RC_OK; - } - ``` - - The **CreateStream\(\)** interface calls the HDI to configure and create a stream. Specifically, the interface first calls the HDI to obtain a **StreamOperation** object and then creates a **StreamInfo** object. Call **CreateStreams\(\)** and **CommitStreams\(\)** to create and configure a stream. - - ``` - RetCode Hos3516Demo::CreateStreams(const int streamIdSecond, StreamIntent intent) - { - std::vector> streamInfos; - std::vector>().swap(streamInfos); - GetStreamOpt(); // Obtain a StreamOperator object. - std::shared_ptr previewStreamInfo = std::make_shared(); - SetStreamInfo(previewStreamInfo, streamCustomerPreview_, STREAM_ID_PREVIEW, PREVIEW); // Fill in the StreamInfo. - if (previewStreamInfo->bufferQueue_ == nullptr) { - CAMERA_LOGE("demo test: CreateStream CreateProducer(); is nullptr\n"); - return RC_ERROR; - } - streamInfos.push_back(previewStreamInfo); - - std::shared_ptr secondStreamInfo = std::make_shared(); - if (streamIdSecond == STREAM_ID_CAPTURE) { - SetStreamInfo(secondStreamInfo, streamCustomerCapture_, STREAM_ID_CAPTURE, intent); - } else { - SetStreamInfo(secondStreamInfo, streamCustomerVideo_, STREAM_ID_VIDEO, intent); - } - if (secondStreamInfo->bufferQueue_ == nullptr) { - CAMERA_LOGE("demo test: CreateStreams CreateProducer() secondStreamInfo is nullptr\n"); - return RC_ERROR; - } - streamInfos.push_back(secondStreamInfo); - - rc = streamOperator_->CreateStreams(streamInfos); // Create a stream. - if (rc != Camera::NO_ERROR) { - CAMERA_LOGE("demo test: CreateStream CreateStreams error\n"); - return RC_ERROR; - } - - rc = streamOperator_->CommitStreams(Camera::NORMAL, ability_); // Commit the stream. - if (rc != Camera::NO_ERROR) { - CAMERA_LOGE("demo test: CreateStream CommitStreams error\n"); - std::vector streamIds = {STREAM_ID_PREVIEW, streamIdSecond}; - streamOperator_->ReleaseStreams(streamIds); - return RC_ERROR; - } - return RC_OK; - } - ``` - - The **CaptureON\(\)** interface calls the **Capture\(\)** method of **StreamOperator** to obtain camera data, rotate the buffer, and start a thread to receive data of the corresponding type. - - ``` - RetCode Hos3516Demo::CaptureON(const int streamId, const int captureId, CaptureMode mode) - { - The std::shared_ptr captureInfo = std::make_shared(); // Create and fill in CaptureInfo. - captureInfo->streamIds_ = {streamId}; - captureInfo->captureSetting_ = ability_; - captureInfo->enableShutterCallback_ = false; - - The int rc = streamOperator_->Capture(captureId, captureInfo, true); // The stream capture starts, and buffer recycling starts. - if (mode == CAPTURE_PREVIEW) { - streamCustomerPreview_->ReceiveFrameOn(nullptr); // Create a preview thread to receive the passed buffers. - } else if (mode == CAPTURE_SNAPSHOT) { - The streamCustomerCapture_->ReceiveFrameOn([this](void* addr, const uint32_t size) { // Create a capture thread to receive the passed buffers through the StoreImage callback. - StoreImage(addr, size); - }); - } else if (mode == CAPTURE_VIDEO) { - OpenVideoFile(); - streamCustomerVideo_->ReceiveFrameOn([this](void* addr, const uint32_t size) {// Create a video thread to receive the passed buffer by calling the StoreVideo callback. - StoreVideo(addr, size); - }); - } - return RC_OK; - } - ``` - -3. Implement the **ManuList\(\)** function to obtain characters from the console through the **fgets\(\)** interface. Different characters correspond to different capabilities provided by the demo, and the functionality menu is printed. - - ``` - static void ManuList(const std::shared_ptr& mainDemo, - const int argc, char** argv) - { - int idx, c; - int awb = 1; - constexpr char shortOptions[] = "h:cwvaqof:"; - c = getopt_long(argc, argv, shortOptions, longOptions, &idx); - while(1) { - switch (c) { - case 'h': - c = PutMenuAndGetChr(); // Print the menu. - break; - - case 'f': - FlashLightTest(mainDemo); // Test the flashlight capability. - c = PutMenuAndGetChr(); - break; - case 'o': - OfflineTest(mainDemo); // Test the offline capability. - c = PutMenuAndGetChr(); - break; - case 'c': - CaptureTest(mainDemo); // Test the capture capability. - c = PutMenuAndGetChr(); - break; - case 'w': // Test the AWB capability. - if (awb) { - mainDemo->SetAwbMode(OHOS_CAMERA_AWB_MODE_INCANDESCENT); - } else { - mainDemo->SetAwbMode(OHOS_CAMERA_AWB_MODE_OFF); - } - awb = !awb; - c = PutMenuAndGetChr(); - break; - case 'a': // Test the AE capability. - mainDemo->SetAeExpo(); - c = PutMenuAndGetChr(); - break; - case 'v': // Test the video capability. - VideoTest(mainDemo); - c = PutMenuAndGetChr(); - break; - case 'q': // Exit the demo. - PreviewOff(mainDemo); - mainDemo->QuitDemo(); - exit(EXIT_SUCCESS); - - default: - CAMERA_LOGE("main test: command error please retry input command"); - c = PutMenuAndGetChr(); - break; - } - } - } - ``` - - The **PutMenuAndGetChr\(\)** interface prints the menu of the demo and calls **fgets\(\)** to wait for commands from the console. - - ``` - static int PutMenuAndGetChr(void) - { - constexpr uint32_t inputCount = 50; - int c = 0; - char strs[inputCount]; - Usage(stdout); - CAMERA_LOGD("pls input command(input -q exit this app)\n"); - fgets(strs, inputCount, stdin); - - for (int i = 0; i < inputCount; i++) { - if (strs[i] != '-') { - c = strs[i]; - break; - } - } - return c; - } - ``` - - The console outputs the menu details as follows: - - ``` - "Options:\n" - "-h | --help Print this message\n" - "-o | --offline stream offline test\n" - "-c | --capture capture one picture\n" - "-w | --set WB Set white balance Cloudy\n" - "-v | --video capture Viedeo of 10s\n" - "-a | --Set AE Set Auto exposure\n" - "-f | --Set Flashlight Set flashlight ON 5s OFF\n" - "-q | --quit stop preview and quit this app\n"); - ``` - - Other capabilities in the demo are implemented by calling different HDIs, which are similar to **PreviewOn\(\)**. For details, see [ohos\_camera\_demo](https://gitee.com/openharmony/drivers_peripheral/tree/master/camera/hal/init). - - + Use the **Close()** interface in the **CameraDeviceImpl** class to close the camera device. This interface calls **PowerDown()** in the **DeviceManager** to power off the device. + +### Development Example + +There is a camera demo in the **/drivers/peripheral/camera/hal/init** directory. After system startup, the executable file **ohos_camera_demo** is generated in the **/vendor/bin** directory. This demo can implement basic camera capabilities such as preview and photographing. The following uses the demo as an example to describe how to use the HDI to write the **PreviewOn()** and **CaptureON()** instances. For details, see [ohos_camera_demo](https://gitee.com/openharmony/drivers_peripheral/tree/master/camera/hal/init). + +1. Construct a CameraDemo object in the **main** function. This object contains methods for initializing the camera and starting, stopping, and releasing streams. The **mainDemo->InitSensors()** function is used to initialize the **CameraHost**, and the **mainDemo->InitCameraDevice()** function is used to initialize the **CameraDevice**. + + ``` + int main(int argc, char** argv) + { + RetCode rc = RC_OK; + auto mainDemo = std::make_shared(); + rc = mainDemo->InitSensors(); // Initialize the CameraHost. + if (rc == RC_ERROR) { + CAMERA_LOGE("main test: mainDemo->InitSensors() error\n"); + return -1; + } + + rc = mainDemo->InitCameraDevice(); // Initialize the CameraDevice. + if (rc == RC_ERROR) { + CAMERA_LOGE("main test: mainDemo->InitCameraDevice() error\n"); + return -1; + } + + rc = PreviewOn(0, mainDemo); // Configure and enable streams. + if (rc != RC_OK) { + CAMERA_LOGE("main test: PreviewOn() error demo exit"); + return -1; + } + + ManuList(mainDemo, argc, argv); // Print the menu to the console. + + return RC_OK; + } + ``` + + The function used to initialize the **CameraHost** is implemented as follows, where the HDI **ICameraHost::Get()** is called to obtain the **demoCameraHost** and set the callback: + + ``` + RetCode CameraDemo::InitSensors() + { + demoCameraHost_ = ICameraHost::Get(DEMO_SERVICE_NAME); + if (demoCameraHost_ == nullptr) { + CAMERA_LOGE("demo test: ICameraHost::Get error"); + return RC_ERROR; + } + + hostCallback_ = new CameraHostCallback(); + rc = demoCameraHost_->SetCallback(hostCallback_); + return RC_OK; + } + ``` + + The implementation of the function for initializing the **CameraDevice** is as follows, where the **GetCameraIds(cameraIds_)**, **GetCameraAbility(cameraId, ability_)**, and **OpenCamera(cameraIds_.front(), callback, demoCameraDevice_)** interfaces are called to obtain the **demoCameraHost**. + + ``` + RetCode CameraDemo::InitCameraDevice() + { + (void)demoCameraHost_->GetCameraIds(cameraIds_); + const std::string cameraId = cameraIds_.front(); + demoCameraHost_->GetCameraAbility(cameraId, ability_); + + sptr callback = new CameraDeviceCallback(); + rc = demoCameraHost_->OpenCamera(cameraIds_.front(), callback, demoCameraDevice_); + return RC_OK; + } + ``` + +2. Implement the **PreviewOn()** interface to configure streams, enable preview streams, and start stream capture. After this interface is called, the camera preview channel starts running. Two streams are enabled: preview stream and capture or video stream. Only the preview stream will be captured. + + ``` + static RetCode PreviewOn(int mode, const std::shared_ptr& mainDemo) + { + rc = mainDemo->StartPreviewStream(); // Configure the preview stream. + if (mode == 0) { + rc = mainDemo->StartCaptureStream(); // Configure the capture stream. + } else { + rc = mainDemo->StartVideoStream(); // Configure the video stream. + } + + rc = mainDemo->CaptureON(STREAM_ID_PREVIEW, CAPTURE_ID_PREVIEW, CAPTURE_PREVIEW); // Capture the preview stream. + return RC_OK; + } + ``` + + The **StartCaptureStream()**, **StartVideoStream()**, and **StartPreviewStream()** interfaces call the **CreateStream()** interface with different input parameters. + + ``` + RetCode CameraDemo::StartVideoStream() + { + RetCode rc = RC_OK; + if (isVideoOn_ == 0) { + isVideoOn_ = 1; + rc = CreateStream(STREAM_ID_VIDEO, streamCustomerVideo_, VIDEO); // To enable the preview stream or capture stream, change the input parameters. + } + return RC_OK; + } + ``` + + The **CreateStream()** interface calls the HDI to configure and create a stream. Specifically, the interface first calls the HDI to obtain a **StreamOperation** object and then creates a **StreamInfo** object. Call **CreateStreams()** and **CommitStreams()** to create and configure a stream. + + ``` + RetCode CameraDemo::CreateStreams(const int streamIdSecond, StreamIntent intent) + { + std::vector> streamInfos; + std::vector>().swap(streamInfos); + GetStreamOpt(); // Obtain a StreamOperator object. + std::shared_ptr previewStreamInfo = std::make_shared(); + SetStreamInfo(previewStreamInfo, streamCustomerPreview_, STREAM_ID_PREVIEW, PREVIEW); // Fill in the StreamInfo. + if (previewStreamInfo->bufferQueue_ == nullptr) { + CAMERA_LOGE("demo test: CreateStream CreateProducer(); is nullptr\n"); + return RC_ERROR; + } + streamInfos.push_back(previewStreamInfo); + + std::shared_ptr secondStreamInfo = std::make_shared(); + if (streamIdSecond == STREAM_ID_CAPTURE) { + SetStreamInfo(secondStreamInfo, streamCustomerCapture_, STREAM_ID_CAPTURE, intent); + } else { + SetStreamInfo(secondStreamInfo, streamCustomerVideo_, STREAM_ID_VIDEO, intent); + } + + if (secondStreamInfo->bufferQueue_ == nullptr) { + CAMERA_LOGE("demo test: CreateStreams CreateProducer() secondStreamInfo is nullptr\n"); + return RC_ERROR; + } + streamInfos.push_back(secondStreamInfo); + + rc = streamOperator_->CreateStreams(streamInfos); // Create a stream. + if (rc != Camera::NO_ERROR) { + CAMERA_LOGE("demo test: CreateStream CreateStreams error\n"); + return RC_ERROR; + } + + rc = streamOperator_->CommitStreams(Camera::NORMAL, ability_); + if (rc != Camera::NO_ERROR) { + CAMERA_LOGE("demo test: CreateStream CommitStreams error\n"); + std::vector streamIds = {STREAM_ID_PREVIEW, streamIdSecond}; + streamOperator_->ReleaseStreams(streamIds); + return RC_ERROR; + } + return RC_OK; + } + ``` + + The **CaptureON()** interface calls the **Capture()** interface of **StreamOperator** to obtain camera data, rotate the buffer, and start a thread to receive data of the corresponding type. + + ``` + RetCode CameraDemo::CaptureON(const int streamId, const int captureId, CaptureMode mode) + { + std::shared_ptr captureInfo = std::make_shared(); // Create and fill in CaptureInfo. + captureInfo->streamIds_ = {streamId}; + captureInfo->captureSetting_ = ability_; + captureInfo->enableShutterCallback_ = false; + + int rc = streamOperator_->Capture(captureId, captureInfo, true);// The stream capture starts, and buffer recycling starts. + if (mode == CAPTURE_PREVIEW) { + streamCustomerPreview_->ReceiveFrameOn(nullptr); // Create a preview thread to receive the passed buffers. + } else if (mode == CAPTURE_SNAPSHOT) { + streamCustomerCapture_->ReceiveFrameOn([this](void* addr, const uint32_t size) { // Create a capture thread to receive the passed buffers through the StoreImage callback. + StoreImage(addr, size); + }); + } else if (mode == CAPTURE_VIDEO) { + OpenVideoFile(); + streamCustomerVideo_->ReceiveFrameOn([this](void* addr, const uint32_t size) {// Create a video thread to receive the passed buffer by calling the StoreVideo callback. + StoreVideo(addr, size); + }); + } + return RC_OK; + } + ``` + +3. Implement the **ManuList()** function to obtain characters from the console through the **fgets()** interface. Different characters correspond to different capabilities provided by the demo, and the functionality menu is printed. + + ``` + static void ManuList(const std::shared_ptr& mainDemo, + const int argc, char** argv) + { + int idx, c; + int awb = 1; + constexpr char shortOptions[] = "h:cwvaqof:"; + c = getopt_long(argc, argv, shortOptions, longOptions, &idx); + while(1) { + switch (c) { + case 'h': + c = PutMenuAndGetChr(); // Print the menu. + break; + + case 'f': + FlashLightTest(mainDemo); // Test the flashlight capability. + c = PutMenuAndGetChr(); + break; + case 'o': + OfflineTest(mainDemo); // Test the offline capability. + c = PutMenuAndGetChr(); + break; + case 'c': + CaptureTest(mainDemo); // Test the capture capability. + c = PutMenuAndGetChr(); + break; + case 'w': // Test the AWB capability. + if (awb) { + mainDemo->SetAwbMode(OHOS_CAMERA_AWB_MODE_INCANDESCENT); + } else { + mainDemo->SetAwbMode(OHOS_CAMERA_AWB_MODE_OFF); + } + awb = !awb; + c = PutMenuAndGetChr(); + break; + case 'a': // Test the AE capability. + mainDemo->SetAeExpo(); + c = PutMenuAndGetChr(); + break; + case 'v': // Test the video capability. + VideoTest(mainDemo); + c = PutMenuAndGetChr(); + break; + case 'q': // Exit the demo. + PreviewOff(mainDemo); + mainDemo->QuitDemo(); + exit(EXIT_SUCCESS); + + default: + CAMERA_LOGE("main test: command error please retry input command"); + c = PutMenuAndGetChr(); + break; + } + } + } + ``` + + The **PutMenuAndGetChr()** interface prints the menu of the demo and calls **fgets()** to wait for commands from the console. + + ``` + static int PutMenuAndGetChr(void) + { + constexpr uint32_t inputCount = 50; + int c = 0; + char strs[inputCount]; + Usage(stdout); + CAMERA_LOGD("pls input command(input -q exit this app)\n"); + fgets(strs, inputCount, stdin); + + for (int i = 0; i < inputCount; i++) { + if (strs[i] != '-') { + c = strs[i]; + break; + } + } + return c; + } + ``` + + The console outputs the menu details as follows: + + ``` + "Options:\n" + "-h | --help Print this message\n" + "-o | --offline stream offline test\n" + "-c | --capture capture one picture\n" + "-w | --set WB Set white balance Cloudy\n" + "-v | --video capture Viedeo of 10s\n" + "-a | --Set AE Set Auto exposure\n" + "-f | --Set Flashlight Set flashlight ON 5s OFF\n" + "-q | --quit stop preview and quit this app\n"); + ``` + + diff --git a/en/device-dev/driver/figures/camera-driver-model-architecture.png b/en/device-dev/driver/figures/camera-driver-model-architecture.png index 772356a3efede5b1b8b45b27270ed79fcf9e9d01..1a4fa27e98e5bb4568be1399b57da3b987ee4fbf 100644 GIT binary patch literal 27028 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