|
@@ -0,0 +1,447 @@
|
|
|
+import image from '@ohos.multimedia.image';
|
|
|
+import fs from "@ohos.file.fs"
|
|
|
+import CommonEventManager from '@ohos.commonEventManager'
|
|
|
+import uploadInstance from '../../common/util/UploadUtil';
|
|
|
+import router from '@ohos.router';
|
|
|
+import fileIo from '@ohos.file.fs';
|
|
|
+const TAG = "Process camera upload"
|
|
|
+@Component
|
|
|
+export struct MultiMediaCollect {
|
|
|
+ @State isExpanded: boolean = false
|
|
|
+ @State selectedIndex:number = 0
|
|
|
+ @State shootButtonClick:number =1
|
|
|
+
|
|
|
+ @State isUploading: boolean = false
|
|
|
+ @State commodityPixelMap: PixelMap | null = null;
|
|
|
+ @State isButtonPressed: boolean = false;
|
|
|
+ @State isCapturing: boolean = false;
|
|
|
+ @State isStopView: boolean = false;
|
|
|
+ @State reminds:string = ''
|
|
|
+ @State readTimer:number = 0
|
|
|
+ private previewManager: PreviewManager | null = null; // 初始化为 null
|
|
|
+
|
|
|
+ subscriber: CommonEventManager.CommonEventSubscriber | null = null;
|
|
|
+ subscribeInfo: CommonEventManager.CommonEventSubscribeInfo = { events: ["sonycamera_callback"] };
|
|
|
+ //订阅回调(code=1代表连接成功,code=2代表拍照成功)
|
|
|
+ createSubscriber = async () => {
|
|
|
+ this.subscriber = await CommonEventManager.createSubscriber(this.subscribeInfo);
|
|
|
+ if (this.subscriber) {
|
|
|
+ console.info(TAG,"创建订阅回调成功");
|
|
|
+ CommonEventManager.subscribe(this.subscriber, (err, data) => {
|
|
|
+ if (err?.code) {
|
|
|
+ console.error(TAG,"SubscribeCallBack err=" + JSON.stringify(err));
|
|
|
+ } else {
|
|
|
+ console.info(TAG,"SubscribeCallBack data=" + JSON.stringify(data));
|
|
|
+ if (data && data.code&&data.code === 2) {
|
|
|
+ this.uploadPhoto();//拍照成功->上传照片
|
|
|
+ }else if (data && data.code&&data.code === 3) {
|
|
|
+ console.info(TAG,"开始预览" );
|
|
|
+ this.startView();//连接成功开始预览
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ };
|
|
|
+ //连接相机
|
|
|
+ connectCamera = async () => {
|
|
|
+ CommonEventManager.publish("opensession", (err) => {
|
|
|
+ if (err?.code) {
|
|
|
+ console.info(TAG,"Publish openSession err=" + JSON.stringify(err))
|
|
|
+ } else {
|
|
|
+ console.info(TAG,"Publish openSession succeed ")
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ //开始预览
|
|
|
+ startView=()=>{
|
|
|
+ CommonEventManager.publish("startview", (err) => {
|
|
|
+ if (err?.code) {
|
|
|
+ console.error(TAG, JSON.stringify(err));
|
|
|
+ } else {
|
|
|
+ this.isCapturing = false;
|
|
|
+ this.isStopView =false;
|
|
|
+ this.liveShow();
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ //重连相机
|
|
|
+ reconnectCamera=()=>{
|
|
|
+ CommonEventManager.publish("reconnect", (err) => {
|
|
|
+ if (err?.code) {
|
|
|
+ console.info("[CommonEvent] Publish CallBack err=" + JSON.stringify(err))
|
|
|
+ } else {
|
|
|
+ console.info("[CommonEvent] Publish reconnect succeed ")
|
|
|
+ AlertDialog.show(
|
|
|
+ {
|
|
|
+ title: '请重新插入相机USB线缆',
|
|
|
+ message: '请重新插入相机USB线缆',
|
|
|
+ confirm: {
|
|
|
+ value: '确定',
|
|
|
+ action: () => {
|
|
|
+ console.info('执行重新连接操作');
|
|
|
+ router.back();
|
|
|
+ // 这里可以添加重连逻辑
|
|
|
+ }
|
|
|
+ },
|
|
|
+ cancel: () => {
|
|
|
+ console.info('用户取消操作');
|
|
|
+ }
|
|
|
+ }
|
|
|
+ )
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ //开始预览 50ms一张图片(20帧)
|
|
|
+ liveShow = async () => {
|
|
|
+ this.previewManager = new PreviewManager(getContext(this));
|
|
|
+ let index = 0;
|
|
|
+ this.readTimer = setInterval(async () => {
|
|
|
+ if (!this.isStopView && !this.isCapturing) {
|
|
|
+ try {
|
|
|
+ const success = await this.previewManager!.processFrame(index);
|
|
|
+ if (success) {
|
|
|
+ this.commodityPixelMap = this.previewManager!.getActiveBuffer();
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.warn(TAG, "预览更新失败:", error);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }, 50);
|
|
|
+ };
|
|
|
+
|
|
|
+ //断开相机(停止预览->关闭连接)
|
|
|
+ disconnectCamera = () => {
|
|
|
+ CommonEventManager.publish("stopview", (err) => {
|
|
|
+ if (err?.code) {
|
|
|
+ console.info(TAG,"Publish stopview err=" + JSON.stringify(err))
|
|
|
+ } else {
|
|
|
+ console.info(TAG,"Publish stopview succeed ")
|
|
|
+ CommonEventManager.publish("closesession", (err) => {
|
|
|
+ if (err?.code) {
|
|
|
+ console.info(TAG,"Publish closesession err=" + JSON.stringify(err))
|
|
|
+ } else {
|
|
|
+ console.info(TAG,"Publish closesession succeed ")
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ //拍照(停止预览->拍照->开始预览)
|
|
|
+ takePhoto = async () => {
|
|
|
+ this.isStopView = true;
|
|
|
+ this.isCapturing = true;
|
|
|
+ CommonEventManager.publish("stopview", (err) => {
|
|
|
+ if (err?.code) {
|
|
|
+ console.info(TAG,"Publish stopview err=" + JSON.stringify(err));
|
|
|
+ this.isCapturing = false;
|
|
|
+ } else {
|
|
|
+ console.info(TAG,"Publish Publish stopview succeed");
|
|
|
+ CommonEventManager.publish("shootonly", (err) => {
|
|
|
+ if (err?.code) {
|
|
|
+ console.info(TAG,"Publish shootonly error=" + JSON.stringify(err));
|
|
|
+ this.isCapturing = false;
|
|
|
+ } else {
|
|
|
+ console.info(TAG,"Publish Publish shootonly succeed");
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ });
|
|
|
+ };
|
|
|
+
|
|
|
+ setFlashMode = (mode: 'openflash' | 'closeflash' | 'autoflash') => {
|
|
|
+ this.isStopView = true;
|
|
|
+ CommonEventManager.publish("stopview", (err) => {
|
|
|
+ if (err?.code) {
|
|
|
+ console.info(TAG,"Publish stopview err=" + JSON.stringify(err));
|
|
|
+ } else {
|
|
|
+ console.info(TAG,"Publish Publish stopview succeed");
|
|
|
+ CommonEventManager.publish(mode, (err) => {
|
|
|
+ if (err?.code) {
|
|
|
+ console.info(TAG,"Publish shootonly error=" + JSON.stringify(err));
|
|
|
+ } else {
|
|
|
+ console.info(TAG,"Publish Publish shootonly succeed");
|
|
|
+ CommonEventManager.publish("startview", (err) => {
|
|
|
+ this.isStopView = false;
|
|
|
+ if (err?.code) {
|
|
|
+ console.info(TAG,"Publish shootonly error=" + JSON.stringify(err));
|
|
|
+ }else{
|
|
|
+ console.info(TAG,"Publish Publish shootonly succeed");
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ //上传照片
|
|
|
+ uploadPhoto=()=>{
|
|
|
+ let imageUri: string = "/data/storage/el2/base/haps/entry/files/image_base64.txt"
|
|
|
+ try {
|
|
|
+ uploadInstance.startUploadBase64(imageUri, () => {
|
|
|
+ this.isUploading = false
|
|
|
+ this.startView();//上传完恢复预览
|
|
|
+ })
|
|
|
+ } catch (error) {
|
|
|
+ console.error(TAG,"upload failed:", error.code);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ async aboutToAppear() {
|
|
|
+ await this.createSubscriber();
|
|
|
+ await this.connectCamera();
|
|
|
+ await this.liveShow();
|
|
|
+ }
|
|
|
+
|
|
|
+ aboutToDisappear(): void {
|
|
|
+ this.isStopView = true;
|
|
|
+ sleep(100)
|
|
|
+ this.disconnectCamera()
|
|
|
+ if (this.subscriber) {
|
|
|
+ CommonEventManager.unsubscribe(this.subscriber);
|
|
|
+ }
|
|
|
+ if (this.readTimer) {
|
|
|
+ clearInterval(this.readTimer);
|
|
|
+ }
|
|
|
+ if (this.commodityPixelMap) {
|
|
|
+ this.commodityPixelMap.release();
|
|
|
+ this.commodityPixelMap = null;
|
|
|
+ }
|
|
|
+ if (this.previewManager) {
|
|
|
+ this.previewManager.release();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ build() {
|
|
|
+ Row() {
|
|
|
+ Stack(){
|
|
|
+ if (this.commodityPixelMap) {
|
|
|
+ Image(this.commodityPixelMap)
|
|
|
+ .width('100%')
|
|
|
+ .height('100%')
|
|
|
+ .objectFit(ImageFit.Fill)
|
|
|
+ }
|
|
|
+ if(this.isUploading){
|
|
|
+ Column() {
|
|
|
+ Text('正在上传中...')
|
|
|
+ .fontSize($r('app.float.fontSize_30'))
|
|
|
+ .margin({bottom:'10%'})
|
|
|
+ LoadingProgress()
|
|
|
+ .color(Color.Blue)
|
|
|
+ .width('25%')
|
|
|
+ .width('25%')
|
|
|
+ }
|
|
|
+ .height('30%')
|
|
|
+ .width('25%')
|
|
|
+ .backgroundColor(Color.White)
|
|
|
+ .borderRadius($r('app.float.virtualSize_16'))
|
|
|
+ .justifyContent(FlexAlign.Center)
|
|
|
+ }
|
|
|
+ Row() {
|
|
|
+ Image(
|
|
|
+ this.selectedIndex === 0 ? $r('app.media.process_flash_auto') :
|
|
|
+ this.selectedIndex === 1 ? $r('app.media.process_flash_open') :
|
|
|
+ $r('app.media.process_flash_close')
|
|
|
+ )
|
|
|
+ .width($r('app.float.virtualSize_48'))
|
|
|
+ .height($r('app.float.virtualSize_48'))
|
|
|
+ .enabled(!this.isUploading)
|
|
|
+ .onClick(() => {
|
|
|
+ this.isExpanded = !this.isExpanded
|
|
|
+ })
|
|
|
+ .margin({right:'5%'})
|
|
|
+ if (this.isExpanded) {
|
|
|
+ Row() {
|
|
|
+ Row(){
|
|
|
+ Text('自动')
|
|
|
+ .fontSize($r('app.float.fontSize_24'))
|
|
|
+ .fontColor(this.selectedIndex===0?$r('app.color.FFFFFF'):$r('app.color.60FFFFFF'))
|
|
|
+ }
|
|
|
+ .width('33.3%')
|
|
|
+ .height('100%')
|
|
|
+ .justifyContent(FlexAlign.Center)
|
|
|
+ .borderRadius($r('app.float.virtualSize_16'))
|
|
|
+ .backgroundColor(this.selectedIndex===0?$r('app.color.0A84FF'):$r('app.color.60000000'))
|
|
|
+ .onClick(() => {
|
|
|
+ this.setFlashMode('autoflash')
|
|
|
+ this.isExpanded = false
|
|
|
+ this.selectedIndex = 0
|
|
|
+ })
|
|
|
+ Row(){
|
|
|
+ Text('开启')
|
|
|
+ .fontSize($r('app.float.fontSize_24'))
|
|
|
+ .fontColor(this.selectedIndex===1?$r('app.color.FFFFFF'):$r('app.color.60FFFFFF'))
|
|
|
+ }
|
|
|
+ .width('33.3%')
|
|
|
+ .height('100%')
|
|
|
+ .justifyContent(FlexAlign.Center)
|
|
|
+ .borderRadius($r('app.float.virtualSize_16'))
|
|
|
+ .backgroundColor(this.selectedIndex===1?$r('app.color.0A84FF'):$r('app.color.60000000'))
|
|
|
+ .onClick(() => {
|
|
|
+ this.setFlashMode('openflash')
|
|
|
+ this.isExpanded = false
|
|
|
+ this.selectedIndex = 1
|
|
|
+ })
|
|
|
+
|
|
|
+ Row(){
|
|
|
+ Text('关闭')
|
|
|
+ .fontSize($r('app.float.fontSize_24'))
|
|
|
+ .fontColor(this.selectedIndex===2?$r('app.color.FFFFFF'):$r('app.color.60FFFFFF'))
|
|
|
+ }
|
|
|
+ .width('33.3%')
|
|
|
+ .height('100%')
|
|
|
+ .justifyContent(FlexAlign.Center)
|
|
|
+ .borderRadius($r('app.float.virtualSize_16'))
|
|
|
+ .backgroundColor(this.selectedIndex===2?$r('app.color.0A84FF'):$r('app.color.60000000'))
|
|
|
+ .onClick(() => {
|
|
|
+ this.setFlashMode('closeflash')
|
|
|
+ this.isExpanded = false
|
|
|
+ this.selectedIndex = 2
|
|
|
+ })
|
|
|
+ }
|
|
|
+ .animation({ duration: 300, curve: Curve.EaseOut }) // 展开动画
|
|
|
+ .backgroundColor($r('app.color.60000000'))
|
|
|
+ .borderRadius($r('app.float.virtualSize_16'))
|
|
|
+ .width('85%')
|
|
|
+ .height('100%')
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .justifyContent(FlexAlign.Start)
|
|
|
+ .alignItems(VerticalAlign.Bottom)
|
|
|
+ .position({ x: '2%', y: '92%' })
|
|
|
+ .height('5%')
|
|
|
+ .width('24%')
|
|
|
+ Image(this.shootButtonClick===1?$r('app.media.process_no_shoot'):$r('app.media.process_shoot'))
|
|
|
+ .width($r('app.float.virtualSize_88'))
|
|
|
+ .height($r('app.float.virtualSize_88'))
|
|
|
+ .scale({ x: this.shootButtonClick, y: this.shootButtonClick })
|
|
|
+ .borderRadius($r('app.float.fontSize_16'))
|
|
|
+ .enabled(!this.isUploading)
|
|
|
+ .animation({
|
|
|
+ duration: 200,
|
|
|
+ curve: Curve.Linear
|
|
|
+ })
|
|
|
+ .onClick(() => {
|
|
|
+ this.shootButtonClick = 0.9;
|
|
|
+ setTimeout(() => {
|
|
|
+ this.shootButtonClick = 1;
|
|
|
+ }, 200);
|
|
|
+ if(this.isUploading){
|
|
|
+ return
|
|
|
+ }
|
|
|
+ this.isUploading = true
|
|
|
+ this.takePhoto();
|
|
|
+ })
|
|
|
+ .position({ x: '48%', y: '90%' })
|
|
|
+ }
|
|
|
+ .width('80%')
|
|
|
+ .height('100%')
|
|
|
+ .backgroundColor($r('app.color.000000'))
|
|
|
+ .border({width:2,color:$r('app.color.10FFFFFF')})
|
|
|
+ }
|
|
|
+ .width('100%')
|
|
|
+ .height('100%')
|
|
|
+ .backgroundColor($r('app.color.10FFFFFF'))
|
|
|
+ .borderRadius($r('app.float.fontSize_16'))
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function sleep(ms: number): Promise<void> {
|
|
|
+ return new Promise((resolve) => setTimeout(resolve, ms));
|
|
|
+}
|
|
|
+@CustomDialog
|
|
|
+struct RemindDialog {
|
|
|
+ controller: CustomDialogController
|
|
|
+ @Link remind: string
|
|
|
+ build() {
|
|
|
+ Column() {
|
|
|
+ Text(this.remind)
|
|
|
+ .fontSize(20)
|
|
|
+ .margin({ top: 10, bottom: 10 })
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function delayExecution(callback: Function, delay: number) {
|
|
|
+ let timerId = setInterval(() => {
|
|
|
+ clearInterval(timerId);
|
|
|
+ callback();
|
|
|
+ }, delay);
|
|
|
+}
|
|
|
+
|
|
|
+class PreviewManager {
|
|
|
+ private context: Context;
|
|
|
+ private activeBuffer: PixelMap | null = null;
|
|
|
+ private nextBuffer: PixelMap | null = null;
|
|
|
+ private isProcessing: boolean = false;
|
|
|
+ private lastFrameTime: number = 0;
|
|
|
+ private frameInterval: number = 50;
|
|
|
+
|
|
|
+ constructor(context: Context) {
|
|
|
+ this.context = context;
|
|
|
+ }
|
|
|
+
|
|
|
+ async processFrame(index: number): Promise<boolean> {
|
|
|
+ if (this.isProcessing) return false;
|
|
|
+
|
|
|
+ const now = Date.now();
|
|
|
+ if (now - this.lastFrameTime < this.frameInterval) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ this.isProcessing = true;
|
|
|
+ try {
|
|
|
+ const filePath = `${this.context.filesDir}/live${index}.jpg`;
|
|
|
+
|
|
|
+ // 检查文件是否存在
|
|
|
+ try {
|
|
|
+ fs.accessSync(filePath);
|
|
|
+ } catch {
|
|
|
+ this.isProcessing = false;
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ const imageSource = image.createImageSource(filePath);
|
|
|
+ if (!imageSource) {
|
|
|
+ this.isProcessing = false;
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 创建较小尺寸的PixelMap以提高性能
|
|
|
+
|
|
|
+ this.nextBuffer = await imageSource.createPixelMap();
|
|
|
+ imageSource.release();
|
|
|
+
|
|
|
+ // 交换缓冲区
|
|
|
+ if (this.activeBuffer) {
|
|
|
+ this.activeBuffer.release();
|
|
|
+ }
|
|
|
+ this.activeBuffer = this.nextBuffer;
|
|
|
+ this.nextBuffer = null;
|
|
|
+ this.lastFrameTime = now;
|
|
|
+ return true;
|
|
|
+ } catch (error) {
|
|
|
+ console.warn(TAG, "处理预览帧失败:", error);
|
|
|
+ return false;
|
|
|
+ } finally {
|
|
|
+ this.isProcessing = false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ getActiveBuffer(): PixelMap | null {
|
|
|
+ return this.activeBuffer;
|
|
|
+ }
|
|
|
+
|
|
|
+ release() {
|
|
|
+ if (this.activeBuffer) {
|
|
|
+ this.activeBuffer.release();
|
|
|
+ this.activeBuffer = null;
|
|
|
+ }
|
|
|
+ if (this.nextBuffer) {
|
|
|
+ this.nextBuffer.release();
|
|
|
+ this.nextBuffer = null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|