一、前言
本文主要介绍如何使用鸿蒙官方camera示例代码,在AI Camera开发板(Hi3516DV300)上进行拍照和录像演示,目的为理解鸿蒙操作系统应用框架,熟悉鸿蒙开发板上应用调试流程,使得能够对HarmonyOS的摄像头控制有更深入的了解,后续可参考此官方示例进行“AI智能相机”等设备产品开发。
二、配置编译camera示例代码
1,HarmonyOS官方SDK的camera示例代码位于:applications/sample/camera/media/camera_sample.cpp。
2,查看对应的BUILD.gn文件发现编译后的可执行文件输出路径为out/dev_tools目录;
如果想将camera_sample可执行程序生产到bin目录可修改为output_dir= "$root_out_dir/"。
3,需要注意,开发板启动后默认会加载launcher应用,应用的图形界面默认显示在媒体图层上方,会影响camera_sample的演示结果,因此需要在编译或是打包时去掉launcher应用。
修改方法:将“applications/sample/camera/hap/BUILD.gn”中copy("copy_hap") {}中 "//applications/sample/camera/hap/launcher.hap",整行注释。
三、拍照和录像核心代码分析
拍照开发步骤
1,实现设备状态回调的派生类,用户在设备状态发生变更(如新插入相机设备/相机掉线)时,自定义操作。
- class SampleCameraDeviceCallback : public CameraDeviceCallback {
- void OnCameraStatus(std::string cameraId, int32_t status) override
- {
- //do something when camera is available/unavailable
- }
- };
复制代码 2,实现帧事件回调的派生类,这里在拿到帧数据以后将其转存为文件。
- static void SampleSaveCapture(const char *p, uint32_t size)
- {
- cout << "Start saving picture" << endl;
- struct timeval tv;
- gettimeofday(&tv, NULL);
- struct tm *ltm = localtime(&tv.tv_sec);
- if (ltm != nullptr) {
- ostringstream ss("Capture_");
- ss << "Capture" << ltm->tm_hour << "-" << ltm->tm_min << "-" << ltm->tm_sec << ".jpg";
- ofstream pic("/sdcard/" + ss.str(), ofstream::out | ofstream::trunc);
- cout << "write " << size << " bytes" << endl;
- pic.write(p, size);
- cout << "Saving picture end" << endl;
- }
- }
- class TestFrameStateCallback : public FrameStateCallback {
- void OnFrameFinished(Camera &camera, FrameConfig &fc, FrameResult &result) override
- {
- cout << "Receive frame complete inform." << endl;
- if (fc.GetFrameConfigType() == FRAME_CONFIG_CAPTURE) {
- cout << "Capture frame received." << endl;
- list<Surface *> surfaceList = fc.GetSurfaces();
- for (Surface *surface : surfaceList) {
- SurfaceBuffer *buffer = surface->AcquireBuffer();
- if (buffer != nullptr) {
- char *virtAddr = static_cast<char *>(buffer->GetVirAddr());
- if (virtAddr != nullptr) {
- SampleSaveCapture(virtAddr, buffer->GetSize());
- }
- surface->ReleaseBuffer(buffer);
- }
- delete surface;
- }
- delete &fc;
- }
- }
- };
复制代码 3,实现相机状态回调的派生类,当相机状态发生变化(配置成功/失败,创建成功/失败)时,自定义操作。
- class SampleCameraStateMng : public CameraStateCallback {
- public:
- SampleCameraStateMng() = delete;
- SampleCameraStateMng(EventHandler &eventHdlr) : eventHdlr_(eventHdlr) {}
- ~SampleCameraStateMng()
- {
- if (recordFd_ != -1) {
- close(recordFd_);
- }
- }
- void OnCreated(Camera &c) override
- {
- cout << "Sample recv OnCreate camera." << endl;
- auto config = CameraConfig::CreateCameraConfig();
- config->SetFrameStateCallback(&fsCb_, &eventHdlr_);
- c.Configure(*config);
- cam_ = &c;
- }
- void OnCreateFailed(const std::string cameraId, int32_t errorCode) override {}
- void OnReleased(Camera &c) override {}
- };
复制代码 4,创建CameraKit,用于创建和获取camera信息。
- CameraKit *camKit = CameraKit::GetInstance();
- list<string> camList = camKit->GetCameraIds();
- string camId;
- for (auto &cam : camList) {
- cout << "camera name:" << cam << endl;
- const CameraAbility *ability = camKit->GetCameraAbility(cam);
- /* find camera which fits user's ability */
- list<CameraPicSize> sizeList = ability->GetSupportedSizes(0);
- if (find(sizeList.begin(), sizeList.end(), CAM_PIC_1080P) != sizeList.end()) {
- camId = cam;
- break;
- }
- }
复制代码 5,创建Camera实例。
- EventHandler eventHdlr; // Create a thread to handle callback events
- SampleCameraStateMng CamStateMng(eventHdlr);
- camKit->CreateCamera(camId, CamStateMng, eventHdlr);
复制代码 6,根据步骤1、步骤2、步骤3中的回调设计,camera实例创建成功后会进行配置操作,主流程中app需要设计同步机制。
- void OnCreated(Camera &c) override
- {
- cout << "Sample recv OnCreate camera." << endl;
- auto config = CameraConfig::CreateCameraConfig();
- config->SetFrameStateCallback(&fsCb_, &eventHdlr_);
- c.Configure(*config);
- cam_ = &c;
- }
- void Capture()
- {
- if (cam_ == nullptr) {
- cout << "Camera is not ready." << endl;
- return;
- }
- FrameConfig *fc = new FrameConfig(FRAME_CONFIG_CAPTURE);
- Surface *surface = Surface::CreateSurface();
- if (surface == nullptr) {
- delete fc;
- }
- surface->SetWidthAndHeight(1920, 1080); /* 1920:width,1080:height */
- fc->AddSurface(*surface);
- cam_->TriggerSingleCapture(*fc);
- }
复制代码 录像步骤
1,1-4步和拍照开发步骤相同。
2,获取录像FrameConfig。
- /* 从recorder获取surface */
- Surface *surface = recorder_->GetSurface(0);
- surface->SetWidthAndHeight(1920, 1080);
- surface->SetQueueSize(3);
- surface->SetSize(1024 * 1024);
- /* 将surface配置到帧配置中 */
- FrameConfig *fc = new FrameConfig(FRAME_CONFIG_RECORD);
- fc->AddSurface(*surface);
复制代码 3,开启和停止录像。
- stateCallback->camera_->TriggerLoopingCapture(*fc); // 开始录像
- stateCallback->camera_->StopLoopingCapture(); // 结束录像
复制代码
三、调试运行
1,通过TF卡拷贝到开发板运行
将可执行文件camera_sample拷贝到TF卡中,然后插入TF卡到开发板,如果先插TF到开发板再开机会自动挂载TF到/sdcard目录,如果是先开机再插入TF卡,则需要手动进行挂载TF卡,手动挂载TF卡方法如下:
2,如果没有TF卡或读卡器,可以将编译出来的camera_sample可执行文件,生产到rootfs.img中,然后烧录到开发板进行运行。需要修改applications/sample/camera/media/BUILD.gn文件
将output_dir = "$root_out_dir/dev_ools"修改为output_dir = "$root_out_dir/"即可。
重新执行源码仓编译并烧写入单板后,可在单板bin目录下找到camera_sample文件。
3,在开发板运行
如上图,运行示例程序后
按1进行拍照,拍照的文件格式为jpg,存储在/sdcard,文件名Capture*;
按2进行录像,录像的文件格式为mp4,存储在/sdcard,文件名Record*,按s键停止;
按q可退出程序。