一、前言
本文主要介绍如何使用鸿蒙官方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可退出程序。