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, { Filter, ConflictFiles } 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 EventLog from '../utils/EventLog'; 26import { GlobalContext } from '../utils/GlobalContext'; 27 28const TAG = '[SaveCameraAsset]:'; 29 30export type FetchOpType = { 31 fetchColumns: Array<string>, 32 predicates: dataSharePredicates.DataSharePredicates, 33}; 34 35type FileMessageType = { 36 fileId: string; 37 bufferLength: number; 38}; 39 40export default class SaveCameraAsset { 41 private lastSaveTime = ''; 42 private saveIndex = 0; 43 private mUserFileManager: UserFileManager.UserFileManager; 44 public videoPrepareFile: UserFileManager.FileAsset; 45 private lastFileMessage: FileMessageType = { 46 fileId: '', bufferLength: 0 47 }; 48 private mCameraAlbum: UserFileManager.Album; 49 private photoUri: string = ''; 50 private videoUri: string = ''; 51 52 constructor() { 53 this.mUserFileManager = UserFileManager.getUserFileMgr(GlobalContext.get().getCameraAbilityContext()); 54 } 55 56 public getPhotoUri() { 57 Log.log(`${TAG} getPhotoUri= ${this.photoUri}`); 58 return this.photoUri; 59 } 60 61 private async createCameraAlbum(): Promise<void> { 62 Log.log(`${TAG} createCameraAlbum E`); 63 if (!this.mCameraAlbum) { 64 let fetchResult = await this.mUserFileManager?.getAlbums(UserFileManager.AlbumType.SYSTEM, UserFileManager.AlbumSubType.CAMERA); 65 this.mCameraAlbum = await fetchResult?.getFirstObject(); 66 Log.log(`${TAG} createCameraAlbum albumUri: ${JSON.stringify(this.mCameraAlbum.albumUri)}`); 67 } 68 Log.log(`${TAG} createCameraAlbum X`); 69 } 70 71 public saveImage(mReceiver, thumbWidth: number, thumbHeight: number, thumbnailGetter: ThumbnailGetter, captureCallBack: FunctionCallBack): void { 72 Log.info(`${TAG} saveImage E mediaLibrary.getMediaLibrary media: ${this.mUserFileManager}`); 73 mReceiver.on('imageArrival', async () => { 74 Log.start(Log.UPDATE_PHOTO_THUMBNAIL); 75 Log.log(`${TAG} saveImage ImageReceiver on called`); 76 const buffer = await this.getBufferByReceiver(mReceiver); 77 if (!buffer) { 78 return; 79 } 80 captureCallBack.onCapturePhotoOutput(); 81 82 const fileAsset: UserFileManager.FileAsset = await this.createAsset(UserFileManager.FileType.IMAGE); 83 if (!fileAsset) { 84 Log.info(`${TAG} fileAsset is null`); 85 return; 86 } 87 // @ts-ignore 88 await fileAsset.setPending(true); 89 this.lastFileMessage = { 90 fileId: fileAsset.uri, bufferLength: buffer.byteLength 91 }; 92 this.photoUri = fileAsset.uri; 93 Log.info(`${TAG} saveImage photoUri: ${this.photoUri}`); 94 await this.fileAssetOperate(fileAsset, async (fd: number) => { 95 Log.info(`${TAG} saveImage fileio write begin`); 96 try { 97 fs.writeSync(fd, buffer); 98 fs.fsyncSync(fd); 99 } catch (e) { 100 Log.error(`${TAG} fileAssetOperate fileio writeSync ${JSON.stringify(e)}`); 101 } 102 Log.info(`${TAG} saveImage fileio write done`); 103 }).catch(error => { 104 Log.error(`${TAG} saveImage error: ${JSON.stringify(error)}`); 105 }); 106 // @ts-ignore 107 await fileAsset.setPending(false); 108 thumbnailGetter.getThumbnailInfo(thumbWidth, thumbHeight, this.photoUri).then(thumbnail => { 109 Log.info(`${TAG} saveImage thumbnailInfo: ${thumbnail}`); 110 captureCallBack.onCaptureSuccess(thumbnail, this.photoUri); 111 Log.end(Log.UPDATE_PHOTO_THUMBNAIL); 112 }) 113 }) 114 Log.info(`${TAG} saveImage X`); 115 } 116 117 public async getThumbnailInfo(width: number, height: number): Promise<image.PixelMap | undefined> { 118 Log.info(`${TAG} getThumbnailInfo E width: ${width}, height: ${height}`); 119 Log.info(`${TAG} getThumbnailInfo E`); 120 const fileAsset: UserFileManager.FileAsset = await this.getLastFileAsset(); 121 if (!fileAsset) { 122 Log.info(`${TAG} getThumbnailInfo getLastFileAsset error: fileAsset undefined.`); 123 return undefined; 124 } 125 let thumbnailSize: image.Size = { 126 width: width, height: height 127 }; 128 let thumbnailPixelMap: image.PixelMap = <image.PixelMap> await fileAsset.getThumbnail(thumbnailSize).catch(e => { 129 Log.error(`${TAG} getThumbnail error: ${JSON.stringify(e)}`); 130 }); 131 if (thumbnailPixelMap === undefined) { 132 Log.info(`${TAG} getThumbnail fail`); 133 } else { 134 Log.info(`${TAG} getThumbnail successful ` + thumbnailPixelMap); 135 } 136 Log.info(`${TAG} getThumbnailInfo X`); 137 return thumbnailPixelMap; 138 } 139 140 public async getLastFileAsset(): Promise<UserFileManager.FileAsset> { 141 let predicates = new dataSharePredicates.DataSharePredicates(); 142 predicates.orderByDesc('date_added').limit(1, 0); 143 let fetchOptions: FetchOpType = { 144 fetchColumns: ['date_added'], 145 predicates: predicates, 146 }; 147 Log.info(`${TAG} getLastFileAsset fetchOp: ${JSON.stringify(fetchOptions)}`); 148 return this.getFileAssetByFetchOp(fetchOptions); 149 } 150 151 public async getFileAssetByFetchOp(fetchOp: FetchOpType): Promise<UserFileManager.FileAsset> { 152 let fetchResult; 153 let fileAsset; 154 try { 155 await this.createCameraAlbum(); 156 fetchResult = await this.mCameraAlbum?.getPhotoAssets(fetchOp); 157 if (fetchResult !== undefined) { 158 Log.info(`${TAG} getFileAssetByFetchOp fetchResult success`); 159 fileAsset = await fetchResult.getLastObject(); 160 if (fileAsset !== undefined) { 161 Log.info(`${TAG} getFileAssetByFetchOp fileAsset.displayName : ` + fileAsset.displayName); 162 } 163 } 164 } catch (e) { 165 Log.error(`${TAG} getFileAssetByFetchOp get fileAsset error: ${JSON.stringify(e)}`); 166 } finally { 167 fetchResult.close(); 168 } 169 return fileAsset; 170 } 171 172 private async fileAssetOperate(fileAsset: UserFileManager.FileAsset, operate: (fd: number) => void): Promise<void> { 173 const fd: number = <number> await fileAsset.open('Rw').catch(error => { 174 Log.error(`${TAG} fileAsset open error: ${JSON.stringify(error)}`); 175 return; 176 }); 177 try { 178 await operate.apply(this, [fd]); 179 } catch (error) { 180 Log.error(`${TAG} fileAsset operate error: ${JSON.stringify(error)}`); 181 } finally { 182 Log.info(`${TAG} fileAsset operate close`); 183 await fileAsset.close(fd).catch(error => { 184 EventLog.write(EventLog.SAVE_FAIL); 185 Log.error(`${TAG} fileAsset open error: ${JSON.stringify(error)}`); 186 }); 187 } 188 } 189 190 private async getBufferByReceiver(mReceiver: image.ImageReceiver): Promise<ArrayBuffer | undefined> { 191 const imageInfo: image.Image = <image.Image> await mReceiver.readNextImage().catch(error => { 192 Log.error(`${TAG} saveImage receiver read next image error: ${JSON.stringify(error)}`); 193 return undefined; 194 }); 195 try { 196 const img: image.Component = await imageInfo.getComponent(image.ComponentType.JPEG); 197 if (img && img.byteBuffer) { 198 const buffer: ArrayBuffer = img.byteBuffer.slice(0, img.byteBuffer.byteLength); 199 Log.info(`${TAG} saveImage getComponent img.byteBuffer.byteLength = ${buffer.byteLength}`); 200 return buffer; 201 } else { 202 Log.error(`${TAG} saveImage getComponent img is undefined error`); 203 } 204 } catch (error) { 205 Log.error(`${TAG} saveImage get buffer from receiver error: ${JSON.stringify(error)}`); 206 } finally { 207 if (imageInfo) { 208 await imageInfo.release().catch(error => { 209 Log.error(`${TAG} image info release error: ${JSON.stringify(error)}`); 210 }); 211 } 212 } 213 return undefined; 214 } 215 216 public async createAsset(type: UserFileManager.FileType): Promise<UserFileManager.FileAsset> { 217 const displayName = this.getDisplayName(type); 218 Log.info(`${TAG} createAsset displayName: ${displayName}`); 219 let option: UserFileManager.PhotoCreateOptions = { 220 subType: UserFileManager.PhotoSubType.CAMERA, 221 }; 222 let fileAsset: UserFileManager.FileAsset; 223 try { 224 fileAsset = await this.mUserFileManager.createPhotoAsset(displayName, option); 225 if (fileAsset !== undefined) { 226 Log.info(`${TAG} createPhotoAsset successfully displayName` + fileAsset.displayName); 227 } else { 228 Log.error(`${TAG} createPhotoAsset failed, fileAsset is undefined `); 229 } 230 } catch (e) { 231 Log.error(`${TAG} createPhotoAsset failed, error: ${JSON.stringify(e)}}`); 232 } 233 return fileAsset; 234 } 235 236 private getDisplayName(type: UserFileManager.FileType): string { 237 const mDateTimeUtil: DateTimeUtil = new DateTimeUtil(); 238 const mData: string = mDateTimeUtil.getDate(); 239 const mTime: string = mDateTimeUtil.getTime(); 240 if (type === UserFileManager.FileType.IMAGE) { 241 return `${this.checkName(`IMG_${mData}_${mTime}`)}.jpg`; 242 } else { 243 return `${this.checkName(`VID_${mData}_${mTime}`)}.mp4`; 244 } 245 } 246 247 public async createVideoFd(captureCallBack: VideoCallBack): Promise<number | undefined> { 248 Log.info(`${TAG} createVideoFd E`); 249 const fileAsset: UserFileManager.FileAsset = await this.createAsset(UserFileManager.FileType.VIDEO); 250 if (!fileAsset) { 251 Log.error(`${TAG} createVideoFd mediaLibrary createAsset error: fileAsset undefined.`); 252 return undefined; 253 } 254 let fdNumber: number = 0; 255 try { 256 this.videoPrepareFile = fileAsset; 257 this.videoUri = fileAsset.uri; 258 captureCallBack.videoUri(this.videoUri); 259 Log.info(`${TAG} SaveCameraAsset getLastObject.uri: ${JSON.stringify(fileAsset.uri)}`); 260 fdNumber = await fileAsset.open('Rw'); 261 } catch (err) { 262 Log.error(`${TAG} createVideoFd err: ${err}`); 263 } 264 Log.info(`${TAG} createVideoFd X`); 265 return fdNumber; 266 } 267 268 private checkName(name: string): string { 269 if (this.lastSaveTime === name) { 270 this.saveIndex++; 271 return `${name}_${this.saveIndex}`; 272 } 273 this.lastSaveTime = name; 274 this.saveIndex = 0; 275 return name; 276 } 277}