1/* 2 * Copyright (c) 2023 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16import fs from '@ohos.file.fs'; 17import image from '@ohos.multimedia.image'; 18import UserFileManager from '@ohos.filemanagement.userFileManager'; 19import dataSharePredicates from '@ohos.data.dataSharePredicates'; 20 21import { Log } from '../utils/Log'; 22import DateTimeUtil from '../utils/DateTimeUtil'; 23import { FunctionCallBack, VideoCallBack } from './CameraService'; 24import ThumbnailGetter from './ThumbnailGetter'; 25import ReportUtil from '../utils/ReportUtil'; 26import { GlobalContext } from '../utils/GlobalContext'; 27 28const TAG = '[SaveCameraAsset]:'; 29 30export type FetchOpType = { 31 fetchColumns: Array<string>, 32 predicates: dataSharePredicates.DataSharePredicates, 33}; 34 35export default class SaveCameraAsset { 36 public videoPrepareFile: UserFileManager.FileAsset; 37 private lastSaveTime = ''; 38 private saveIndex = 0; 39 private mUserFileManager: UserFileManager.UserFileManager; 40 private mCameraAlbum: UserFileManager.Album; 41 private photoUri: string = ''; 42 private videoUri: string = ''; 43 44 constructor() { 45 this.mUserFileManager = UserFileManager.getUserFileMgr(GlobalContext.get().getCameraAbilityContext()); 46 } 47 48 public getPhotoUri() { 49 Log.log(`${TAG} getPhotoUri= ${this.photoUri}`); 50 return this.photoUri; 51 } 52 53 public saveImage(mReceiver, thumbWidth: number, thumbHeight: number, thumbnailGetter: ThumbnailGetter, 54 captureCallBack: FunctionCallBack): void { 55 Log.info(`${TAG} saveImage E mediaLibrary.getMediaLibrary media: ${this.mUserFileManager}`); 56 mReceiver.on('imageArrival', async () => { 57 Log.start(Log.UPDATE_PHOTO_THUMBNAIL); 58 Log.log(`${TAG} saveImage ImageReceiver on called`); 59 const buffer = await this.getBufferByReceiver(mReceiver); 60 if (!buffer) { 61 return; 62 } 63 captureCallBack.onCapturePhotoOutput(); 64 let pickerUri = GlobalContext.get().getPickerUri(); 65 Log.info('uri' + pickerUri) 66 if (pickerUri === '' || pickerUri === undefined) { 67 const fileAsset: UserFileManager.FileAsset = await this.createAsset(UserFileManager.FileType.IMAGE); 68 if (!fileAsset) { 69 Log.info(`${TAG} fileAsset is null`); 70 return; 71 } 72 // @ts-ignore 73 await fileAsset.setPending(true); 74 this.photoUri = fileAsset.uri; 75 Log.info(`${TAG} saveImage photoUri: ${this.photoUri}`); 76 await this.fileAssetOperate(fileAsset, async (fd: number) => { 77 Log.info(`${TAG} saveImage fileio write begin`); 78 try { 79 fs.writeSync(fd, buffer); 80 fs.fsyncSync(fd); 81 } catch (e) { 82 Log.error(`${TAG} fileAssetOperate fileio writeSync ${JSON.stringify(e)}`); 83 } 84 Log.info(`${TAG} saveImage fileio write done`); 85 }).catch(error => { 86 Log.error(`${TAG} saveImage error: ${JSON.stringify(error)}`); 87 }); 88 // @ts-ignore 89 await fileAsset.setPending(false); 90 thumbnailGetter.getThumbnailInfo(thumbWidth, thumbHeight, this.photoUri).then(thumbnail => { 91 Log.info(`${TAG} saveImage thumbnailInfo: ${thumbnail}`); 92 captureCallBack.onCaptureSuccess(thumbnail, this.photoUri); 93 Log.end(Log.UPDATE_PHOTO_THUMBNAIL); 94 }) 95 } else { 96 try { 97 Log.info(`${TAG} save Picker image`); 98 const file: fs.File = await fs.open(pickerUri, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE); 99 Log.info(`${TAG} open Picker image byteLength` + buffer.byteLength); 100 await fs.write(file.fd, buffer); 101 Log.info(`${TAG} write Picker image byteLength`); 102 captureCallBack.onCaptureSuccess('thumbnail', pickerUri); 103 } catch (e) { 104 captureCallBack.onCaptureSuccess('thumbnail', ''); 105 Log.error(`${TAG} Picker fileio writeSync error ${JSON.stringify(e)}`); 106 } 107 } 108 }) 109 Log.info(`${TAG} saveImage X`); 110 } 111 112 public async getThumbnailInfo(width: number, height: number): Promise<image.PixelMap | undefined> { 113 Log.info(`${TAG} getThumbnailInfo E width: ${width}, height: ${height}`); 114 Log.info(`${TAG} getThumbnailInfo E`); 115 const fileAsset: UserFileManager.FileAsset = await this.getLastFileAsset(); 116 if (!fileAsset) { 117 Log.info(`${TAG} getThumbnailInfo getLastFileAsset error: fileAsset undefined.`); 118 return undefined; 119 } 120 let thumbnailSize: image.Size = { 121 width: width, height: height 122 }; 123 let thumbnailPixelMap: image.PixelMap = <image.PixelMap> await fileAsset.getThumbnail(thumbnailSize).catch(e => { 124 Log.error(`${TAG} getThumbnail error: ${JSON.stringify(e)}`); 125 }); 126 if (thumbnailPixelMap === undefined) { 127 Log.info(`${TAG} getThumbnail fail`); 128 } else { 129 Log.info(`${TAG} getThumbnail successful ` + thumbnailPixelMap); 130 } 131 Log.info(`${TAG} getThumbnailInfo X`); 132 return thumbnailPixelMap; 133 } 134 135 public async getLastFileAsset(): Promise<UserFileManager.FileAsset> { 136 let predicates = new dataSharePredicates.DataSharePredicates(); 137 predicates.orderByDesc('date_added').limit(1, 0); 138 let fetchOptions: FetchOpType = { 139 fetchColumns: ['date_added'], 140 predicates: predicates, 141 }; 142 Log.info(`${TAG} getLastFileAsset fetchOp: ${JSON.stringify(fetchOptions)}`); 143 return this.getFileAssetByFetchOp(fetchOptions); 144 } 145 146 public async getFileAssetByFetchOp(fetchOp: FetchOpType): Promise<UserFileManager.FileAsset> { 147 let fetchResult; 148 let fileAsset; 149 try { 150 await this.createCameraAlbum(); 151 fetchResult = await this.mCameraAlbum?.getPhotoAssets(fetchOp); 152 if (fetchResult !== undefined) { 153 Log.info(`${TAG} getFileAssetByFetchOp fetchResult success`); 154 fileAsset = await fetchResult.getLastObject(); 155 if (fileAsset !== undefined) { 156 Log.info(`${TAG} getFileAssetByFetchOp fileAsset.displayName : ` + fileAsset.displayName); 157 } 158 } 159 } catch (e) { 160 Log.error(`${TAG} getFileAssetByFetchOp get fileAsset error: ${JSON.stringify(e)}`); 161 } finally { 162 fetchResult.close(); 163 } 164 return fileAsset; 165 } 166 167 public async createAsset(type: UserFileManager.FileType): Promise<UserFileManager.FileAsset> { 168 const displayName = this.getDisplayName(type); 169 Log.info(`${TAG} createAsset displayName: ${displayName}`); 170 let option: UserFileManager.PhotoCreateOptions = { 171 subType: UserFileManager.PhotoSubType.CAMERA, 172 }; 173 let fileAsset: UserFileManager.FileAsset; 174 try { 175 fileAsset = await this.mUserFileManager.createPhotoAsset(displayName, option); 176 if (fileAsset !== undefined) { 177 Log.info(`${TAG} createPhotoAsset successfully displayName` + fileAsset.displayName); 178 } else { 179 Log.error(`${TAG} createPhotoAsset failed, fileAsset is undefined `); 180 } 181 } catch (e) { 182 Log.error(`${TAG} createPhotoAsset failed, error: ${JSON.stringify(e)}}`); 183 } 184 return fileAsset; 185 } 186 187 public async createVideoFd(captureCallBack: VideoCallBack): Promise<number | undefined> { 188 Log.info(`${TAG} createVideoFd E`); 189 const fileAsset: UserFileManager.FileAsset = await this.createAsset(UserFileManager.FileType.VIDEO); 190 if (!fileAsset) { 191 Log.error(`${TAG} createVideoFd mediaLibrary createAsset error: fileAsset undefined.`); 192 return undefined; 193 } 194 let fdNumber: number = 0; 195 try { 196 this.videoPrepareFile = fileAsset; 197 this.videoUri = fileAsset.uri; 198 captureCallBack.videoUri(this.videoUri); 199 Log.info(`${TAG} SaveCameraAsset getLastObject.uri: ${JSON.stringify(fileAsset.uri)}`); 200 fdNumber = await fileAsset.open('Rw'); 201 } catch (err) { 202 Log.error(`${TAG} createVideoFd err: ${err}`); 203 } 204 Log.info(`${TAG} createVideoFd X`); 205 return fdNumber; 206 } 207 208 private async createCameraAlbum(): Promise<void> { 209 Log.log(`${TAG} createCameraAlbum E`); 210 if (!this.mCameraAlbum) { 211 let fetchResult = 212 await this.mUserFileManager?.getAlbums(UserFileManager.AlbumType.SYSTEM, UserFileManager.AlbumSubType.CAMERA); 213 this.mCameraAlbum = await fetchResult?.getFirstObject(); 214 Log.log(`${TAG} createCameraAlbum albumUri: ${JSON.stringify(this.mCameraAlbum.albumUri)}`); 215 } 216 Log.log(`${TAG} createCameraAlbum X`); 217 } 218 219 private async fileAssetOperate(fileAsset: UserFileManager.FileAsset, operate: (fd: number) => void): Promise<void> { 220 const fd: number = <number> await fileAsset.open('Rw').catch(error => { 221 Log.error(`${TAG} fileAsset open error: ${JSON.stringify(error)}`); 222 return; 223 }); 224 try { 225 await operate.apply(this, [fd]); 226 } catch (error) { 227 Log.error(`${TAG} fileAsset operate error: ${JSON.stringify(error)}`); 228 } finally { 229 Log.info(`${TAG} fileAsset operate close`); 230 await fileAsset.close(fd).catch(error => { 231 ReportUtil.write(ReportUtil.SAVE_FAIL); 232 Log.error(`${TAG} fileAsset open error: ${JSON.stringify(error)}`); 233 }); 234 } 235 } 236 237 private async getBufferByReceiver(mReceiver: image.ImageReceiver): Promise<ArrayBuffer | undefined> { 238 const imageInfo: image.Image = <image.Image> await mReceiver.readNextImage().catch(error => { 239 Log.error(`${TAG} saveImage receiver read next image error: ${JSON.stringify(error)}`); 240 return undefined; 241 }); 242 try { 243 const img: image.Component = await imageInfo.getComponent(image.ComponentType.JPEG); 244 if (img && img.byteBuffer) { 245 const buffer: ArrayBuffer = img.byteBuffer.slice(0, img.byteBuffer.byteLength); 246 Log.info(`${TAG} saveImage getComponent img.byteBuffer.byteLength = ${buffer.byteLength}`); 247 return buffer; 248 } else { 249 Log.error(`${TAG} saveImage getComponent img is undefined error`); 250 } 251 } catch (error) { 252 Log.error(`${TAG} saveImage get buffer from receiver error: ${JSON.stringify(error)}`); 253 } finally { 254 if (imageInfo) { 255 await imageInfo.release().catch(error => { 256 Log.error(`${TAG} image info release error: ${JSON.stringify(error)}`); 257 }); 258 } 259 } 260 return undefined; 261 } 262 263 private getDisplayName(type: UserFileManager.FileType): string { 264 const mDateTimeUtil: DateTimeUtil = new DateTimeUtil(); 265 const mData: string = mDateTimeUtil.getDate(); 266 const mTime: string = mDateTimeUtil.getTime(); 267 if (type === UserFileManager.FileType.IMAGE) { 268 return `${this.checkName(`IMG_${mData}_${mTime}`)}.jpg`; 269 } else { 270 return `${this.checkName(`VID_${mData}_${mTime}`)}.mp4`; 271 } 272 } 273 274 private checkName(name: string): string { 275 if (this.lastSaveTime === name) { 276 this.saveIndex++; 277 return `${name}_${this.saveIndex}`; 278 } 279 this.lastSaveTime = name; 280 this.saveIndex = 0; 281 return name; 282 } 283}