鸿蒙OpenHarmony hi3516开发板,标准系统响应按钮拍照
自从搞懂了标准系统GPIO的操作后,即鸿蒙OpenHarmony hi3516开发板,标准系统按钮开关灯,下一步使用按钮拍照也很快的搞定了,先做个暂时的记录吧。下一步,研究如何http调用云服务AI识图
整体开发方式跟上次一样,就不再追溯了,仅仅修改了applications/standard/app/hello.c
重点参考了OpenHarmony的文档和一个test源代码,文档中写了主要的步骤,另外我在代码中把每个步骤都写了注释:
参考文档:multimedia_camera_standard: Implementation of camera device management and camera capture functions | 相机设备和相机采集功能实现
参考源代码:interfaces/innerkits/native/test/camera_capture.cpp · OpenHarmony/multimedia_camera_standard - Gitee.com
使用的代码版本是OH2021-12-25的master源代码,等最近出了新版本tag后,我再验证下这个最新版本是否也OK。
#include#include#include#include#include#include#include "input/camera_input.h"#include "input/camera_manager.h"#include "surface.h"#include #include #include #include #include #include #include #include #include using namespace std;using namespace OHOS;using namespace OHOS::CameraStandard;#define MSG(args...) printf(args) //函数声明static int gpio_export(int pin);static int gpio_unexport(int pin);static int gpio_direction(int pin, int dir);static int gpio_write(int pin, int value);static int gpio_read(int pin);static int gpio_edge(int pin, int edge);static int gpio_export(int pin) { char buffer[64]; int len; int fd; fd = open("/sys/class/gpio/export", O_WRONLY); if (fd < 0) { MSG("Failed to open export for writing!\n"); return(-1); } len = snprintf(buffer, sizeof(buffer), "%d", pin); printf("%s,%d,%d\n",buffer,sizeof(buffer),len); if (write(fd, buffer, len) < 0) { MSG("Failed to export gpio!"); return -1; } close(fd); return 0; } static int gpio_unexport(int pin) { char buffer[64]; int len; int fd; fd = open("/sys/class/gpio/unexport", O_WRONLY); if (fd < 0) { MSG("Failed to open unexport for writing!\n"); return -1; } len = snprintf(buffer, sizeof(buffer), "%d", pin); if (write(fd, buffer, len) IN, 1-->OUTstatic int gpio_direction(int pin, int dir) { static const char dir_str[] = "in\0out"; char path[64]; int fd; snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d/direction", pin); fd = open(path, O_WRONLY); if (fd < 0) { MSG("Failed to open gpio direction for writing!\n"); return -1; } if (write(fd, &dir_str[dir == 0 ? 0 : 3], dir == 0 ? 2 : 3) LOW, 1-->HIGHstatic int gpio_write(int pin, int value) { static const char values_str[] = "01"; char path[64]; int fd; snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d/value", pin); fd = open(path, O_WRONLY); if (fd < 0) { MSG("Failed to open gpio value for writing!\n"); return -1; } if (write(fd, &values_str[value == 0 ? 0 : 1], 1) < 0) { MSG("Failed to write value!\n"); return -1; } close(fd); return 0; }static int gpio_read(int pin) { char path[64]; char value_str[3]; int fd; snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d/value", pin); fd = open(path, O_RDONLY); if (fd < 0) { MSG("Failed to open gpio value for reading!\n"); return -1; } if (read(fd, value_str, 3) none, 1-->rising, 2-->falling, 3-->bothstatic int gpio_edge(int pin, int edge){const char dir_str[] = "none\0rising\0falling\0both"; int ptr;char path[64]; int fd; switch(edge){ case 0: ptr = 0; break; case 1: ptr = 5; break; case 2: ptr = 12; break; case 3: ptr = 20; break; default: ptr = 0;}snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d/edge", pin); fd = open(path, O_WRONLY); if (fd < 0) { MSG("Failed to open gpio edge for writing!\n"); return -1; } if (write(fd, &dir_str[ptr], strlen(&dir_str[ptr])) < 0) { MSG("Failed to set edge!\n"); return -1; } close(fd); return 0; }enum class mode_ { MODE_PREVIEW = 0, MODE_PHOTO};// 获取当前时间static uint64_t GetCurrentLocalTimeStamp(){ std::chrono::time_point tp = std::chrono::time_point_cast(std::chrono::system_clock::now()); auto tmp = std::chrono::duration_cast(tp.time_since_epoch()); return tmp.count();}// 保存图片static int32_t SaveYUV(mode_ mode, const char *buffer, int32_t size){ static const std::int32_t FILE_PERMISSION_FLAG = 00766; char path[PATH_MAX] = {0}; int32_t retVal; if (mode == mode_::MODE_PREVIEW) { system("mkdir -p /data/preview"); retVal = sprintf_s(path, sizeof(path) / sizeof(path[0]), "/data/preview/%s_%lld.yuv", "preview", GetCurrentLocalTimeStamp()); } else { system("mkdir -p /data/capture"); retVal = sprintf_s(path, sizeof(path) / sizeof(path[0]), "/data/capture/%s_%lld.jpg", "photo", GetCurrentLocalTimeStamp()); } if (retVal < 0) { printf("Path Assignment failed"); return -1; } printf("%s, saving file to %s", __FUNCTION__, path); int imgFd = open(path, O_RDWR | O_CREAT, FILE_PERMISSION_FLAG); if (imgFd == -1) { printf("%s, open file failed, errno = %s.", __FUNCTION__, strerror(errno)); return -1; } int ret = write(imgFd, buffer, size); if (ret == -1) { printf("%s, write file failed, error = %s.", __FUNCTION__, strerror(errno)); close(imgFd); return -1; } close(imgFd); return 0;}// 1.创建缓冲区消费者端监听器(CaptureSurfaceListener)以保存图像。class CaptureSurfaceListener : public IBufferConsumerListener {public: mode_ mode; sptr surface_; void OnBufferAvailable() override { int32_t flushFence = 0; int64_t timestamp = 0; OHOS::Rect damage; // initialize the damage OHOS::sptr buffer = nullptr; surface_->AcquireBuffer(buffer, flushFence, timestamp, damage); if (buffer != nullptr) { char *addr = static_cast(buffer->GetVirAddr()); int32_t size = buffer->GetSize(); // Save the buffer(addr) to a file. SaveYUV(mode, addr, size); surface_->ReleaseBuffer(buffer, -1); } }};int main() { int gpio_fd, ret; struct pollfd fds[1]; char buff[10]; //41为红灯, 1为1号按键 gpio_unexport(41); gpio_unexport(1); //41红灯亮起 gpio_export(41); gpio_direction(41, 1);//output out gpio_write(41, 1); //1按钮初始化 gpio_export(1); gpio_direction(1, 0);//input in gpio_edge(1,2); gpio_fd = open("/sys/class/gpio/gpio1/value",O_RDONLY); if(gpio_fd < 0) { MSG("Failed to open value!\n"); return -1; } fds[0].fd = gpio_fd; fds[0].events = POLLPRI; // 参考Camera组件拍摄 https://gitee.com/openharmony/multimedia_camera_standard // 样例:foundation/multimedia/camera_standard/interfaces/innerkits/native/test/camera_capture.cpp // 2.获取相机管理器实例并获取相机对象列表。 int32_t intResult = -1; sptr camManagerObj = CameraManager::GetInstance(); std::vector<sptr> cameraObjList = camManagerObj->GetCameras(); if (cameraObjList.size() GetID().c_str()); } //3. 创建采集会话。 sptr captureSession = camManagerObj->CreateCaptureSession(); if (captureSession == nullptr) { printf("Failed to create capture session"); return -1; } //4. 开始配置采集会话。 captureSession->BeginConfig(); //5. 使用相机对象创建相机输入。 sptr photoOutput; sptr cameraInput = camManagerObj->CreateCameraInput(cameraObjList[0]); if (cameraInput != nullptr) { //6.将相机输入添加到采集会话。 intResult = captureSession->AddInput(cameraInput); if (intResult == 0) { //7.创建消费者Surface并注册监听器以监听缓冲区更新。拍照的宽和高可以配置为所支持的 1280x960 分辨率。 sptr photoSurface = Surface::CreateSurfaceAsConsumer(); photoSurface->SetDefaultWidthAndHeight(1280, 960); sptr capturelistener = new CaptureSurfaceListener(); capturelistener->mode = mode_::MODE_PHOTO; capturelistener->surface_ = photoSurface; photoSurface->RegisterConsumerListener((sptr &)capturelistener); //8.使用上面创建的 Surface 创建拍照输出。 photoOutput = camManagerObj->CreatePhotoOutput(photoSurface); if (photoOutput == nullptr) { printf("Failed to create PhotoOutput"); return -1; } //9.将拍照输出添加到采集会话。 intResult = captureSession->AddOutput(photoOutput); if (intResult != 0) { printf("Failed to Add output to session, intResult: %d", intResult); return -1; } //10. 将配置提交到采集会话。 intResult = captureSession->CommitConfig(); if (intResult != 0) { printf("Failed to Commit config, intResult: %d", intResult); return -1; } } } while(1) { ret = poll(fds,1,5000); if( ret == -1 ) MSG("poll\n"); if( fds[0].revents & POLLPRI) { ret = lseek(gpio_fd,0,SEEK_SET); if( ret == -1 ) MSG("lseek\n"); ret = read(gpio_fd,buff,10);//读取按钮值,但这里没使用 if( ret == -1 ) MSG("read\n"); //切换红灯 int status = gpio_read(41); printf("41 = %d\n",status); gpio_write(41, 1 - status); //11.拍摄照片。 intResult = ((sptr &)photoOutput)->Capture(); if (intResult != 0) { printf("Failed to capture, intResult: %d", intResult); return -1; }else{ printf("Success to capture, intResult: %d", intResult); } //gpio_write(44, cnt++%2); printf("**********************************\n"); } printf("one loop\n"); //usleep(5); } //12. 释放采集会话资源。 captureSession->Release(); return 0;}
Build.gn 引入camera子系统的头文件及模块
import("//build/ohos.gni")import("//drivers/adapter/uhdf2/uhdf.gni")ohos_executable("madixin") { sources = [ "madixin.cpp" ] subsystem_name = "applications" part_name = "prebuilt_hap" include_dirs = [ "//foundation/multimedia/camera_standard/interfaces/innerkits/native/camera/include", "//foundation/multimedia/camera_standard/interfaces/innerkits/native/test", "//foundation/multimedia/camera_standard/services/camera_service/include", "//foundation/multimedia/camera_standard/services/camera_service/binder/base/include", "//foundation/multimedia/camera_standard/services/camera_service/binder/client/include", "//foundation/multimedia/camera_standard/services/camera_service/binder/server/include", "//foundation/graphic/standard/frameworks/surface/include", "//foundation/multimedia/camera_standard/frameworks/innerkitsimpl/metadata/include", "//utils/system/safwk/native/include", "//drivers/framework/include/utils", "//drivers/adapter/uhdf2/osal/include", "//drivers/adapter/uhdf2/include/hdi", "//drivers/peripheral/display/interfaces/include", "//drivers/peripheral/camera/interfaces/include", "//drivers/peripheral/camera/interfaces/hdi_ipc", "//drivers/peripheral/camera/interfaces/hdi_ipc/server", "//drivers/peripheral/camera/interfaces/hdi_ipc/callback/device", "//drivers/peripheral/camera/interfaces/hdi_ipc/callback/operator", "//drivers/peripheral/camera/interfaces/hdi_ipc/callback/host", ] deps = [ "//foundation/graphic/standard:libsurface", "//foundation/multimedia/camera_standard/frameworks/innerkitsimpl/camera:camera_framework", "//foundation/multimedia/camera_standard/frameworks/innerkitsimpl/metadata:metadata", "//utils/native/base:utils", ]}
因为在CaptureSurfaceListener定义了照片保存路径,即/data/capture目录,因此点击按钮拍照后,串口中会输出信息,照片也保存在了/data/capture目录下。
使用命令把照片发到本机就可以浏览拉:
hdc_std file recv /data/capture/photo_4942243.jpg D:\madixin\test\images\
源代码
MyOpenHarmonySample: 我的OpenHarmonySample代码 - Gitee.com