如何調(diào)用設(shè)備攝像頭進(jìn)行拍照、預(yù)覽并將拍攝結(jié)果保存在媒體庫中(Camera)
想了解更多關(guān)于開源的內(nèi)容,請(qǐng)?jiān)L問:
場(chǎng)景說明
調(diào)用設(shè)備攝像頭進(jìn)行拍照、預(yù)覽是許多應(yīng)用開發(fā)過程中都需要的功能。在拍照完成時(shí)顯示照片預(yù)覽圖可以確認(rèn)拍攝的照片是否達(dá)到預(yù)期,本例將為大家介紹如何實(shí)現(xiàn)上述功能。
效果呈現(xiàn)
本例效果如下:
拍照 | 預(yù)覽 |
運(yùn)行環(huán)境
本例基于以下環(huán)境開發(fā),開發(fā)者也可以基于其他適配的版本進(jìn)行開發(fā)。
- IDE:DevEco Studio 4.0.0.201 Beta1
- SDK:Ohos_sdk_public 4.0.7.5 (API Version 10 Beta1)
實(shí)現(xiàn)思路
本例使用@ohos.multimedia.camera接口實(shí)現(xiàn)相機(jī)示例的主要功能:拍照、預(yù)覽;
- 拍照:XComponent組件負(fù)責(zé)繪制攝像頭畫面呈現(xiàn)的窗口,其onload事件調(diào)用cameraModel.ts的initCamera方法初始化相機(jī)功能輸出畫面信息。拍照動(dòng)作使用Image組件實(shí)現(xiàn),其onclick事件調(diào)用cameraModel.ts的takepicture方法開始拍照。
- 預(yù)覽:返回相機(jī)界面點(diǎn)擊底部左側(cè)預(yù)覽圖可進(jìn)入相冊(cè)應(yīng)用,可以在其中查看照片和錄制的視頻。
開發(fā)步驟
申請(qǐng)所需權(quán)限:
在model.json5中添加以下配置:
"requestPermissions": [
{
"name": "ohos.permission.CAMERA"http://允許應(yīng)用使用相機(jī)拍攝照片和錄制視頻
},
{
"name": "ohos.permission.MICROPHONE"http://允許應(yīng)用使用麥克風(fēng)
},
{
"name": "ohos.permission.MEDIA_LOCATION"http://允許應(yīng)用訪問用戶媒體文件中的地理位置信息
},
{
"name": "ohos.permission.WRITE_MEDIA"http://允許應(yīng)用讀寫用戶外部存儲(chǔ)中的媒體文件信息
},
{
"name": "ohos.permission.READ_MEDIA"http://允許應(yīng)用讀取用戶外部存儲(chǔ)中的媒體文件信息
}
]
創(chuàng)建繪制組件XComponent以輸出攝像頭獲取的畫面,其綁定的onload方法中設(shè)定了畫幅的大小。
build() {
Column() {
Title()
.visibility(this.isTitleShow ? Visibility.Visible : Visibility.None)
Stack({ alignContent: Alignment.Bottom }) {
Stack({ alignContent: Alignment.TopStart }) {
XComponent({
id: 'componentId',
type: 'surface',
controller: this.mXComponentController //將控制器綁定至XComponent組件
})
.onLoad(() => {
this.mXComponentController.setXComponentSurfaceSize({ surfaceWidth: 640, surfaceHeight: 480 });//設(shè)置surface大小
this.surfaceId = this.mXComponentController.getXComponentSurfaceId();
this.currentModel = CameraMode.modePhoto;
this.cameraModel.initCamera(this.surfaceId); //調(diào)用model/cameraModel.ts初始化相機(jī)功能
})
.width('100%')
.height('100%')
.margin({ bottom: 152 })
Column() {
}
.width('97%')
.height('100%')
初始化相機(jī)功能initCamera方法通過創(chuàng)建相機(jī)管理器實(shí)例cameraMgr來創(chuàng)建畫面輸出對(duì)象previewOutput。cameraMgr再通過創(chuàng)建CaptureSession實(shí)例來配置會(huì)話,完成相機(jī)功能的準(zhǔn)備工作。
import image from '@ohos.multimedia.image';//自@ohos.multimedia.image引入image,提供圖片處理效果
...
private receiver: image.ImageReceiver = undefined;//圖像接收類,用于獲取組件surface id,接收最新的圖片和讀取下一張圖片
...
constructor() {
this.mediaModel = MediaModel.getMediaInstance();//通過調(diào)用model/MediaModel.ets中的方法創(chuàng)建mediaInstance類mediaModel
//創(chuàng)建ImageReceiver實(shí)例receiver
this.receiver = image.createImageReceiver(
cameraWH.width,
cameraWH.height,
FOUR,
EIGHT
);
//接收?qǐng)D片時(shí)注冊(cè)回調(diào)
this.receiver.on('imageArrival', () => {
//從ImageReceiver讀取下一張圖片
this.receiver.readNextImage((err, image) => {
if (err || image === undefined) {
return;
}
//根據(jù)圖像的組件類型從圖像中獲取組件緩存
image.getComponent(FOUR, (errMsg, img) => {
if (errMsg || img === undefined) {
return;
}
let buffer = new ArrayBuffer(FOUR_THOUSAND_AND_SIXTY_NINE);
if (img.byteBuffer) {
buffer = img.byteBuffer;
}
this.saveImage(buffer, image);
});
});
});
}
async initCamera(surfaceId: string): Promise<void> {
...
try {
this.cameraMgr = camera.getCameraManager(globalThis.cameraContext);//獲取相機(jī)管理器實(shí)例
}
this.camerasArray = this.cameraMgr.getSupportedCameras();//獲取支持指定的相機(jī)設(shè)備對(duì)象
if (this.camerasArray.length === 0) {
return;
}
let mCamera = this.camerasArray[0];
this.cameraInput = this.cameraMgr.createCameraInput(mCamera);
this.cameraInput.open();
this.capability = this.cameraMgr.getSupportedOutputCapability(mCamera);//查詢相機(jī)設(shè)備支持的輸出能力
let previewProfile = this.capability.previewProfiles[0];
//通過相機(jī)管理器創(chuàng)建預(yù)覽輸出對(duì)象
this.previewOutput = this.cameraMgr.createPreviewOutput(
previewProfile,
surfaceId //surfaceId從XComponent組件獲取
);
let rSurfaceId = await this.receiver.getReceivingSurfaceId();//獲取一個(gè)surface id供其他組件使用
let photoProfile = this.capability.photoProfiles[0];
//通過相機(jī)管理器創(chuàng)建照片輸出對(duì)象
this.photoOutPut = this.cameraMgr.createPhotoOutput(
photoProfile,
rSurfaceId //rSurfaceId通過構(gòu)造函數(shù)中定義的圖像接收類receiver獲取
);
this.capSession = this.cameraMgr.createCaptureSession();//創(chuàng)建CaptureSession實(shí)例
this.capSession.beginConfig();//開始配置會(huì)話
this.capSession.addInput(this.cameraInput);//將cameraInput加入會(huì)話
this.capSession.addOutput(this.previewOutput);//將預(yù)覽輸出加入會(huì)話
this.capSession.addOutput(this.photoOutPut);//將照片輸出加入會(huì)話
await this.capSession.commitConfig();//提交配置信息
await this.capSession.start();//開始輸出
}
點(diǎn)擊按鈕進(jìn)行拍照,拍照按鈕通過Image組件呈現(xiàn),其綁定的onClick方法調(diào)用takePicture方法開始拍照。
Image(this.getCameraIcon())
.size({ width: 64, height: 64 })
.margin({ left: 10 })
.id('camera')
.onClick(() => {
if (this.currentModel === CameraMode.modePhoto) {
prompt.showToast({ message: '拍照中...', duration: 200 });
this.cameraModel.takePicture();//調(diào)用model/cameraModel.takePicture()開始拍照
}
})
拍照功能具體實(shí)現(xiàn):
拍照:
async takePicture(): Promise<void> {
//設(shè)置拍照相關(guān)參數(shù)
let photoSettings = {
rotation: this.imageRotation,
quality: camera.QualityLevel.QUALITY_LEVEL_MEDIUM,
location: {
// 位置信息,經(jīng)緯度
latitude: 12.9698,
longitude: 77.75,
altitude: 1000,
},
mirror: false,
};
await this.photoOutPut.capture(photoSettings);
AppStorage.Set('isRefresh', true);
}
保存圖片:
saveImage方法使用MediaModel中的createAndGetUri方法創(chuàng)建Image類型資源,將拍攝到的照片寫入到這個(gè)資源中去。
//model/MediaModel.ts中定義的負(fù)責(zé)保存圖片的相關(guān)方法
async createAndGetUri(mediaType: mediaLibrary.MediaType): Promise<mediaLibrary.FileAsset> {
let dateTimeUtil: DateTimeUtil = new DateTimeUtil();
let info: FileInfo = this.getInfoFromMediaType(mediaType);
let name: string = `${dateTimeUtil.getDate()}_${dateTimeUtil.getTime()}`;//獲取當(dāng)前時(shí)間
let displayName: string = `${info.prefix}${name}${info.suffix}`;
//獲取公共目錄路徑。
let publicPath: string = await this.mediaLibraryTest.getPublicDirectory(
info.directory
);//通過引用自@ohos.multimedia.mediaLibrary的mediaLibraryTest類創(chuàng)建媒體資源,其中定義了媒體類型、名稱、路徑。
let fileAsset: mediaLibrary.FileAsset = await this.mediaLibraryTest.createAsset(
mediaType,//根據(jù)傳入函數(shù)createAndGetUri的mediaType參數(shù)決定創(chuàng)建什么類型的媒體資源
displayName,
publicPath
);
return fileAsset;
}
async getFdPath(fileAsset: mediaLibrary.FileAsset): Promise<number> {
let fd: number = await fileAsset.open('Rw');//打開當(dāng)前文件
return fd;
}
...
async saveImage(buffer: ArrayBuffer, img: image.Image): Promise<void> {
this.fileAsset = await this.mediaModel.createAndGetUri(mediaLibrary.MediaType.IMAGE);
//通過調(diào)用MediaModel中的方法創(chuàng)建Image類型資源
this.photoPath = this.fileAsset.uri;
this.fd = await this.mediaModel.getFdPath(this.fileAsset);
await fileIo.write(this.fd, buffer);//將拍攝的照片寫入到Mediamodel傳回的資源中去
await this.fileAsset.close(this.fd);//釋放open函數(shù)
await img.release();
if (this.takePictureHandle) {
this.takePictureHandle(this.photoPath);
}
}