HarmonyOS深入解析相機的功能和使用

      網友投稿 1079 2022-05-30

      HarmonyOS 相機模塊支持相機業務的開發,開發者可以通過已開放的接口實現相機硬件的訪問、操作和新功能開發,最常見的操作如:預覽、拍照、連拍和錄像等。

      相機靜態能力:用于描述相機的固有能力的一系列參數,比如朝向、支持的分辨率等信息。

      物理相機:物理相機就是獨立的實體攝像頭設備。物理相機ID是用于標志每個物理攝像頭的唯一字串。

      邏輯相機:邏輯相機是多個物理相機組合出來的抽象設備,邏輯相機通過同時控制多個物理相機設備來完成相機某些功能,如大光圈、變焦等功能。邏輯攝像機ID是一個唯一的字符串,標識多個物理攝像機的抽象能力。

      幀捕獲:相機啟動后對幀的捕獲動作統稱為幀捕獲。主要包含單幀捕獲、多幀捕獲、循環幀捕獲。

      單幀捕獲:指的是相機啟動后,在幀數據流中捕獲一幀數據,常用于普通拍照。

      多幀捕獲:指的是相機啟動后,在幀數據流中連續捕獲多幀數據,常用于連拍。

      循環幀捕獲:指的是相機啟動后,在幀數據流中一直捕獲幀數據,常用于預覽和錄像。

      在同一時刻只能有一個相機應用在運行中。

      相機模塊內部有狀態控制,開發者必須按照指導文檔中的流程進行接口的順序調用,否則可能會出現調用失敗等問題。

      為了開發的相機應用擁有更好的兼容性,在創建相機對象或者參數相關設置前請務必進行能力查詢。

      相機模塊主要工作是給相機應用開發者提供基本的相機 API 接口,用于使用相機系統的功能,進行相機硬件的訪問、操作和新功能開發。

      相機的開發流程如圖所示:

      相機模塊為相機應用開發者提供了3個包的內容,包括方法、枚舉、以及常量/變量,方便開發者更容易地實現相機功能。

      相機 API 如下所示:

      HarmonyOS 根據接口所涉數據的敏感程度或所涉能力的安全威脅影響,定義了不同開放范圍與授權方式的權限來保護數據。

      當前權限的開放范圍分為:

      all:所有應用可用

      signature:平臺簽名應用可用

      privileged:預制特權應用可用

      restricted:證書可控應用可用

      應用在使用對應服務的能力或數據時,需要申請對應權限:

      已在 config.json 文件中聲明的非敏感權限,會在應用安裝時自動授予,該類權限的授權方式為系統授權(system_grant)。

      敏感權限需要應用動態申請,通過運行時發送彈窗的方式請求用戶授權,該類權限的授權方式為用戶授權(user_grant)。

      當應用調用服務時,服務會對應用進行權限檢查,如果沒有對應權限則無法使用該服務。

      敏感權限的申請需要按照動態申請流程向用戶申請授權,敏感權限說明如下:

      非敏感權限不涉及用戶的敏感數據或危險操作,僅需在 config.json 中聲明,應用安裝后即被授權。

      受限開放的權限通常是不允許三方應用申請的。如果有特殊場景需要使用,請提供相關申請材料到應用市場申請相應權限證書。如果應用未申請相應的權限證書,卻試圖在 config.json 文件中聲明此類權限,將會導致應用安裝失敗。另外,由于此類權限涉及到用戶敏感數據或危險操作,當應用申請到權限證書后,還需按照動態申請權限的流程向用戶申請授權。受限開放權限說明如下:

      在使用相機之前,需要申請相機的相關權限,保證應用擁有相機硬件及其他功能權限。相機權限列表:

      CameraKit 類是相機的入口 API 類,用于獲取相機設備特性、打開相機,其接口如下表:

      在實現一個相機應用之前必須先創建一個獨立的相機設備,然后才能繼續相機的其他操作。相機設備創建的建議步驟如下:

      通過 CameraKit.getInstance(Context context) 方法獲取唯一的 CameraKit 對象(如果此步驟操作失敗,相機可能被占用或無法使用,如果被占用,必須等到相機釋放后才能重新獲取 CameraKit 對象):

      private void openCamera() { // 獲取CameraKit對象 CameraKit cameraKit = CameraKit.getInstance(context); if (cameraKit == null) { // 處理cameraKit獲取失敗的情況 } }

      HarmonyOS之深入解析相機的功能和使用

      1

      2

      3

      4

      5

      6

      7

      通過 getCameraIds() 方法,獲取當前使用的設備支持的邏輯相機列表。邏輯相機列表中存儲了當前設備擁有的所有邏輯相機 ID,如果列表不為空,則列表中的每個 ID 都支持獨立創建相機對象;否則,說明正在使用的設備無可用的相機,不能繼續后續的操作:

      try { // 獲取當前設備的邏輯相機列表 String[] cameraIds = cameraKit.getCameraIds(); if (cameraIds.length <= 0) { HiLog.error(LABEL, "cameraIds size is 0"); } } catch (IllegalStateException e) { // 處理異常 }

      1

      2

      3

      4

      5

      6

      7

      8

      9

      還可以繼續查詢指定相機 ID 的靜態信息:

      調用 getDeviceLinkType (String physicalId) 方法獲取物理相機連接方式;

      調用 getCameraInfo(String cameraId) 方法查詢相機硬件朝向等信息;

      調用 getCameraAbility(String cameraId) 方法查詢相機能力信息(比如支持的分辨率列表等)。

      CameraInfo 的主要接口:

      CameraAbility 的主要接口:

      通過 createCamera(String cameraId, CameraStateCallback callback, EventHandler handler) 方法,創建相機對象,此步驟執行成功意味著相機系統的硬件已經完成了上電:

      第一個參數 cameraId 可以是上一步獲取的邏輯相機列表中的任何一個相機 ID。

      第二和第三個參數負責相機創建和相機運行時的數據和狀態檢測,請務必保證在整個相機運行周期內有效。

      // 創建相機設備 cameraKit.createCamera(cameraIds[0], cameraStateCallback, eventHandler);

      1

      2

      private final class CameraStateCallbackImpl extends CameraStateCallback { @Override public void onCreated(Camera camera) { // 創建相機設備 } @Override public void onConfigured(Camera camera) { // 配置相機設備 } @Override public void onPartialConfigured(Camera camera) { // 當使用了addDeferredSurfaceSize配置了相機,會接到此回調 } @Override public void onReleased(Camera camera) { // 釋放相機設備 } } // 相機創建和相機運行時的回調 CameraStateCallbackImpl cameraStateCallback = new CameraStateCallbackImpl();

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      21

      22

      23

      24

      import ohos.eventhandler.EventHandler; import ohos.eventhandler.EventRunner; // 執行回調的EventHandler EventHandler eventHandler = new EventHandler(EventRunner.create("CameraCb"));

      1

      2

      3

      4

      5

      至此,相機設備的創建已經完成。相機設備創建成功會在 CameraStateCallback 中觸發 onCreated(Camera camera) 回調。在進入相機設備配置前,請確保相機設備已經創建成功。否則會觸發相機設備創建失敗的回調,并返回錯誤碼,需要進行錯誤處理后,重新執行相機設備的創建。

      創建相機設備成功后,在 CameraStateCallback 中會觸發 onCreated(Camera camera) 回調,并且帶回 Camera 對象,用于執行相機設備的操作。

      當一個新的相機設備成功創建后,首先需要對相機進行配置,調用 configure(CameraConfig) 方法實現配置。相機配置主要是設置預覽、拍照、錄像用到的 Surface(詳見 ohos.agp.graphics.Surface),沒有配置過 Surface,相應的功能不能使用。

      為了進行相機幀捕獲結果的數據和狀態檢測,還需要在相機配置時調用 setFrameStateCallback(FrameStateCallback, EventHandler) 方法設置幀回調。

      private void initSurface() { surfaceProvider = new SurfaceProvider(this); DirectionalLayout.LayoutConfig params = new DirectionalLayout.LayoutConfig( ComponentContainer.LayoutConfig.MATCH_PARENT, ComponentContainer.LayoutConfig.MATCH_PARENT); surfaceProvider.setLayoutConfig(params); surfaceProvider.pinToZTop(false); surfaceProvider.getSurfaceOps().get().addCallback(new SurfaceCallBack()); ((ComponentContainer) findComponentById(ResourceTable.Id_surface_container)).addComponent(surfaceProvider); } private FrameStateCallback frameStateCallbackImpl = new FrameStateCallback(){ @Override public void onFrameStarted(Camera camera, FrameConfig frameConfig, long frameNumber, long timestamp) { ... } @Override public void onFrameProgressed(Camera camera, FrameConfig frameConfig, FrameResult frameResult) { ... } @Override public void onFrameFinished(Camera camera, FrameConfig frameConfig, FrameResult frameResult) { ... } @Override public void onFrameError(Camera camera, FrameConfig frameConfig, int errorCode, FrameResult frameResult) { ... } @Override public void onCaptureTriggerStarted(Camera camera, int captureTriggerId, long firstFrameNumber) { ... } @Override public void onCaptureTriggerFinished(Camera camera, int captureTriggerId, long lastFrameNumber) { ... } @Override public void onCaptureTriggerInterrupted(Camera camera, int captureTriggerId) { ... } }; private final class CameraStateCallbackImpl extends CameraStateCallback { @Override public void onCreated(Camera camera) { cameraDevice = camera; previewSurface = surfaceProvider.getSurfaceOps().get().getSurface(); cameraConfigBuilder = camera.getCameraConfigBuilder(); if (cameraConfigBuilder == null) { HiLog.error(LABEL, "onCreated cameraConfigBuilder is null"); return; } // 配置預覽的Surface cameraConfigBuilder.addSurface(previewSurface); // 配置拍照的Surface cameraConfigBuilder.addSurface(imageReceiver.getRecevingSurface()); // 配置幀結果的回調 cameraConfigBuilder.setFrameStateCallback(frameStateCallbackImpl, handler); try { // 相機設備配置 camera.configure(cameraConfigBuilder.build()); } catch (IllegalArgumentException e) { HiLog.error(LABEL, "Argument Exception"); } catch (IllegalStateException e) { HiLog.error(LABEL, "State Exception"); } } }

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      21

      22

      23

      24

      25

      26

      27

      28

      29

      30

      31

      32

      33

      34

      35

      36

      37

      38

      39

      40

      41

      42

      43

      44

      45

      46

      47

      48

      49

      50

      51

      52

      53

      54

      55

      56

      57

      58

      59

      60

      61

      62

      63

      64

      65

      66

      67

      68

      相機配置成功后,在 CameraStateCallback 中會觸發 onConfigured(Camera camera) 回調,然后才可以執行相機幀捕獲相關的操作。

      CameraConfig.Builder 的主要接口:

      Camera 操作類,包括相機預覽、錄像、拍照等功能接口。Camera 的主要接口如下:

      用戶一般都是先看見預覽畫面才執行拍照或者其他功能,所以對于一個普通的相機應用,預覽是必不可少的。

      啟動預覽的建議步驟如下:

      通過 getFrameConfigBuilder(FRAME_CONFIG_PREVIEW) 方法獲取預覽配置模板,常用幀配置項見下表:

      通過 triggerLoopingCapture(FrameConfig) 方法實現循環幀捕獲(如預覽/錄像):

      private final class CameraStateCallbackImpl extends CameraStateCallback { @Override public void onConfigured(Camera camera) { // 獲取預覽配置模板 frameConfigBuilder = camera.getFrameConfigBuilder(FRAME_CONFIG_PREVIEW); // 配置預覽Surface frameConfigBuilder.addSurface(previewSurface); previewFrameConfig = frameConfigBuilder.build(); try { // 啟動循環幀捕獲 int triggerId = camera.triggerLoopingCapture(previewFrameConfig); } catch (IllegalArgumentException e) { HiLog.error(LABEL, "Argument Exception"); } catch (IllegalStateException e) { HiLog.error(LABEL, "State Exception"); } } }

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      經過以上的操作,相機應用已經可以正常進行實時預覽了。在預覽狀態下,開發者還可以執行其他操作,比如:當預覽幀配置更改時,可以通過 triggerLoopingCapture(FrameConfig) 方法實現預覽幀配置的更新;

      // 預覽幀變焦值變更 frameConfigBuilder.setZoom(1.2f); // 調用triggerLoopingCapture方法實現預覽幀配置更新 triggerLoopingCapture(frameConfigBuilder.build());

      1

      2

      3

      4

      通過 stopLoopingCapture() 方法停止循環幀捕獲(停止預覽):

      // 停止預覽幀捕獲 camera.stopLoopingCapture()

      1

      2

      拍照功能屬于相機應用的最重要功能之一,而且照片質量對用戶至關重要。相機模塊基于相機復雜的邏輯,從應用接口層到器件驅動層都已經默認的做好了最適合用戶的配置,這些默認配置盡可能地保證用戶拍出的每張照片的質量。

      發起拍照的建議步驟如下:

      通過 getFrameConfigBuilder(FRAME_CONFIG_PICTURE) 方法獲取拍照配置模板,并且設置拍照幀配置,如下表:

      拍照前準備圖像幀數據的接收實現:

      // 圖像幀數據接收處理對象 private ImageReceiver imageReceiver; // 執行回調的EventHandler private EventHandler eventHandler = new EventHandler(EventRunner.create("CameraCb")); // 拍照支持分辨率 private Size pictureSize; // 單幀捕獲生成圖像回調Listener private final ImageReceiver.IImageArrivalListener imageArrivalListener = new ImageReceiver.IImageArrivalListener() { @Override public void onImageArrival(ImageReceiver imageReceiver) { StringBuffer fileName = new StringBuffer("picture_"); fileName.append(UUID.randomUUID()).append(".jpg"); // 定義生成圖片文件名 File myFile = new File(dirFile, fileName.toString()); // 創建圖片文件 imageSaver = new ImageSaver(imageReceiver.readNextImage(), myFile); // 創建一個讀寫線程任務用于保存圖片 eventHandler.postTask(imageSaver); // 執行讀寫線程任務生成圖片 } }; // 保存圖片, 圖片數據讀寫,及圖像生成見run方法 class ImageSaver implements Runnable { private final Image myImage; private final File myFile; ImageSaver(Image image, File file) { myImage = image; myFile = file; } @Override public void run() { Image.Component component = myImage.getComponent(ImageFormat.ComponentType.JPEG); byte[] bytes = new byte[component.remaining()]; component.read(bytes); FileOutputStream output = null; try { output = new FileOutputStream(myFile); output.write(bytes); // 寫圖像數據 } catch (IOException e) { HiLog.error(LABEL, "save picture occur exception!"); } finally { if (output != null) { try { output.close(); // 關閉流 } catch (IOException e) { HiLog.error(LABEL, "image release occur exception!"); } } myImage.release(); } } } private void takePictureInit() { List pictureSizes = cameraAbility.getSupportedSizes(ImageFormat.JPEG); // 獲取拍照支持分辨率列表 pictureSize = getpictureSize(pictureSizes) // 根據拍照要求選擇合適的分辨率 imageReceiver = ImageReceiver.create(Math.max(pictureSize.width, pictureSize.height), Math.min(pictureSize.width, pictureSize.height), ImageFormat.JPEG, 5); // 創建ImageReceiver對象,注意create函數中寬度要大于高度;5為最大支持的圖像數,請根據實際設置。 imageReceiver.setImageArrivalListener(imageArrivalListener); }

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      21

      22

      23

      24

      25

      26

      27

      28

      29

      30

      31

      32

      33

      34

      35

      36

      37

      38

      39

      40

      41

      42

      43

      44

      45

      46

      47

      48

      49

      50

      51

      52

      53

      54

      55

      56

      57

      58

      59

      通過 triggerSingleCapture(FrameConfig) 方法實現單幀捕獲(如拍照):

      private void capture() { // 獲取拍照配置模板 framePictureConfigBuilder = cameraDevice.getFrameConfigBuilder(FRAME_CONFIG_PICTURE); // 配置拍照Surface framePictureConfigBuilder.addSurface(imageReceiver.getRecevingSurface()); // 配置拍照其他參數 framePictureConfigBuilder.setImageRotation(90); try { // 啟動單幀捕獲(拍照) cameraDevice.triggerSingleCapture(framePictureConfigBuilder.build()); } catch (IllegalArgumentException e) { HiLog.error(LABEL, "Argument Exception"); } catch (IllegalStateException e) { HiLog.error(LABEL, "State Exception"); } }

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      為了捕獲到質量更高和效果更好的圖片,還可以在幀結果中實時監測自動對焦和自動曝光的狀態,一般而言,在自動對焦完成,自動曝光收斂后的瞬間是發起單幀捕獲的最佳時機。

      連拍功能方便用戶一次拍照獲取多張照片,用于捕捉精彩瞬間。

      同普通拍照的實現流程一致,但連拍需要使用 triggerMultiCapture(List frameConfigs) 方法。

      啟動錄像和啟動預覽類似,但需要另外配置錄像 Surface 才能使用。

      錄像前需要進行音視頻模塊的配置:

      private Source source; // 音視頻源 private AudioProperty.Builder audioPropertyBuilder; // 音頻屬性構造器 private VideoProperty.Builder videoPropertyBuilder; // 視頻屬性構造器 private StorageProperty.Builder storagePropertyBuilder; // 音視頻存儲屬性構造器 private Recorder mediaRecorder; // 錄像操作對象 private String recordName; // 音視頻文件名 private void initMediaRecorder() { videoPropertyBuilder.setRecorderBitRate(10000000); // 設置錄制比特率 int rotation = DisplayManager.getInstance().getDefaultDisplay(this).get().getRotation(); videoPropertyBuilder.setRecorderDegrees(getOrientation(rotation)); // 設置錄像方向 videoPropertyBuilder.setRecorderFps(30); // 設置錄制采樣率 videoPropertyBuilder.setRecorderHeight(Math.min(recordSize.height, recordSize.width)); // 設置錄像支持的分辨率,需保證width > height videoPropertyBuilder.setRecorderWidth(Math.max(recordSize.height, recordSize.width)); videoPropertyBuilder.setRecorderVideoEncoder(Recorder.VideoEncoder.H264); // 設置視頻編碼方式 videoPropertyBuilder.setRecorderRate(30); // 設置錄制幀率 source.setRecorderAudioSource(Recorder.AudioSource.MIC); // 設置錄制音頻源 source.setRecorderVideoSource(Recorder.VideoSource.SURFACE); // 設置視頻窗口 mediaRecorder.setSource(source); // 設置音視頻源 mediaRecorder.setOutputFormat(Recorder.OutputFormat.MPEG_4); // 設置音視頻輸出格式 StringBuffer fileName = new StringBuffer("record_"); // 生成隨機文件名 fileName.append(UUID.randomUUID()).append(".mp4"); recordName = fileName.toString(); File file = new File(dirFile, recordName); // 創建錄像文件對象 storagePropertyBuilder.setRecorderFile(file); // 設置存儲音視頻文件名 mediaRecorder.setStorageProperty(storagePropertyBuilder.build()); audioPropertyBuilder.setRecorderAudioEncoder(Recorder.AudioEncoder.AAC); // 設置音頻編碼格式 mediaRecorder.setAudioProperty(audioPropertyBuilder.build()); // 設置音頻屬性 mediaRecorder.setVideoProperty(videoPropertyBuilder.build()); // 設置視頻屬性 mediaRecorder.prepare(); // 準備錄制 HiLog.info(LABEL, "initMediaRecorder end"); }

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      21

      22

      23

      24

      25

      26

      27

      28

      29

      30

      31

      32

      配置錄像幀,啟動錄像:

      private final class CameraStateCallbackImpl extends CameraStateCallback { @Override public void onConfigured(Camera camera) { // 獲取錄像配置模板 frameConfigBuilder = camera.getFrameConfigBuilder(FRAME_CONFIG_RECORD); // 配置預覽Surface frameConfigBuilder.addSurface(previewSurface); // 配置錄像的Surface mRecorderSurface = mediaRecorder.getVideoSurface(); frameConfigBuilder.addSurface(mRecorderSurface); previewFrameConfig = frameConfigBuilder.build(); try { // 啟動循環幀捕獲 int triggerId = camera.triggerLoopingCapture(previewFrameConfig); } catch (IllegalArgumentException e) { HiLog.error(LABEL, "Argument Exception"); } catch (IllegalStateException e) { HiLog.error(LABEL, "State Exception"); } } }

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      21

      通過 camera.stopLoopingCapture() 方法停止循環幀捕獲(錄像)。

      使用完相機后,必須通過 release() 來關閉相機和釋放資源,否則可能導致其他相機應用無法啟動。一旦相機被釋放,它所提供的操作就不能再被調用,否則會導致不可預期的結果,或是會引發狀態異常。

      相機設備釋放的示例代碼如下:

      private void releaseCamera() { if (camera != null) { // 關閉相機和釋放資源 camera.release(); camera = null; } // 拍照配置模板置空 framePictureConfigBuilder = null; // 預覽配置模板置空 previewFrameConfig = null; }

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      HarmonyOS之演示如何通過相機模塊相關接口實現拍照、錄像等功能。

      API 開發者

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:索引可不是萬能的,來看看什么時候會失效(索引在什么時候會失效)
      下一篇:excel表格復制公式數值不變的解決教程(表格公式復制后數值不變)
      相關文章
      国产成人综合亚洲| 久久久亚洲精华液精华液精华液| 国产精品亚洲а∨天堂2021| 亚洲xxxx18| 亚洲娇小性xxxx| 亚洲六月丁香六月婷婷色伊人| 亚洲AV无码国产精品色午友在线| 中国亚洲女人69内射少妇| 国产啪亚洲国产精品无码| 亚洲色婷婷综合开心网| 亚洲日本中文字幕天堂网| 亚洲成a人片在线观看国产| 亚洲国产成人乱码精品女人久久久不卡 | 国产亚洲免费的视频看| 亚洲三区在线观看无套内射| 亚洲精品无码专区在线在线播放| 亚洲午夜福利717| 亚洲av激情无码专区在线播放| 亚洲性天天干天天摸| 91亚洲国产成人久久精品网站| 久久精品国产亚洲AV麻豆网站| 2022年亚洲午夜一区二区福利 | 亚洲无人区视频大全| 亚洲免费网站在线观看| 亚洲天堂2017无码中文| 亚洲AV综合永久无码精品天堂| MM1313亚洲精品无码久久| 亚洲五月午夜免费在线视频| 亚洲人成图片小说网站| 亚洲日本在线观看| 亚洲av乱码一区二区三区| 亚洲精品久久久久无码AV片软件| 亚洲AV无码男人的天堂| 亚洲成a人一区二区三区| 国产亚洲精品a在线观看| 亚洲高清视频在线观看| 亚洲欧洲精品一区二区三区| 亚洲三级在线观看| 精品韩国亚洲av无码不卡区| 国产亚洲成归v人片在线观看| 亚洲AV午夜成人片|