1# 使用ImageReceiver完成图片接收 2<!--Kit: Image Kit--> 3<!--Subsystem: Multimedia--> 4<!--Owner: @aulight02--> 5<!--Designer: @liyang_bryan--> 6<!--Tester: @xchaosioda--> 7<!--Adviser: @zengyawen--> 8 9图片接收类ImageReceiver用于获取组件surface id,接收最新的图片和读取下一张图片,以及释放ImageReceiver实例。 10 11> **说明:** 12> Receiver作为消费者,需要有对应的生产者提供数据才能实现完整功能。常见的生产者是相机的拍照流或预览流。ImageReceiver只作为图片的接收方、消费者,在ImageReceiver设置的size、format等属性实际上并不会生效,图片createImageReceiver时传入的参数不产生实际影响。图片属性需要在发送方、生产者进行设置,如[相机创建预览流](../../reference/apis-camera-kit/arkts-apis-camera-CameraManager.md#createpreviewoutput)时配置[profile](../../reference/apis-camera-kit/arkts-apis-camera-i.md#profile)。 13 14ImageReceiver可以接收相机预览流中的图片,实现[双路预览](../camera/camera-dual-channel-preview.md)。 15 16ImageReceiver信息相关API的详细介绍请参见[API参考](../../reference/apis-image-kit/arkts-apis-image-ImageReceiver.md)。 17 18## 开发步骤 19 20创建ImageReceiver对象,获取SurfaceId创建预览流,注册图像监听,按需处理预览流每帧图像。 21 221. 创建ImageReceiver对象,通过ImageReceiver对象可获取预览流SurfaceId。 23 24 ```ts 25 import { image } from '@kit.ImageKit'; 26 let imageWidth: number = 1920; // 请使用设备支持profile的size的宽。 27 let imageHeight: number = 1080; // 请使用设备支持profile的size的高。 28 29 async function initImageReceiver():Promise<void>{ 30 // 创建ImageReceiver对象。createImageReceiver的参数不会对接收到的数据产生实际影响。 31 let size: image.Size = { width: imageWidth, height: imageHeight }; 32 let imageReceiver = image.createImageReceiver(size, image.ImageFormat.JPEG, 8); 33 // 获取预览流SurfaceId。 34 let imageReceiverSurfaceId = await imageReceiver.getReceivingSurfaceId(); 35 console.info(`initImageReceiver imageReceiverSurfaceId:${imageReceiverSurfaceId}`); 36 } 37 ``` 38 392. 注册监听处理预览流每帧图像数据:通过ImageReceiver组件中imageArrival事件监听获取底层返回的图像数据,详细的API说明请参考[Image API参考](../../reference/apis-image-kit/arkts-apis-image-ImageReceiver.md)。 40 41 ```ts 42 import { BusinessError } from '@kit.BasicServicesKit'; 43 import { image } from '@kit.ImageKit'; 44 45 function onImageArrival(receiver: image.ImageReceiver): void { 46 // 注册imageArrival监听。 47 receiver.on('imageArrival', () => { 48 // 获取图像。 49 receiver.readNextImage((err: BusinessError, nextImage: image.Image) => { 50 if (err || nextImage === undefined) { 51 console.error('readNextImage failed'); 52 return; 53 } 54 // 解析图像内容。 55 nextImage.getComponent(image.ComponentType.JPEG, async (err: BusinessError, imgComponent: image.Component) => { 56 if (err || imgComponent === undefined) { 57 console.error('getComponent failed'); 58 } 59 if (imgComponent.byteBuffer) { 60 // 详情见下方解析图片buffer数据参考,本示例以方式一为例。 61 let width = nextImage.size.width; // 获取图片的宽。 62 let height = nextImage.size.height; // 获取图片的高。 63 let stride = imgComponent.rowStride; // 获取图片的stride。 64 console.debug(`getComponent with width:${width} height:${height} stride:${stride}`); 65 // stride与width一致。 66 if (stride == width) { 67 let pixelMap = await image.createPixelMap(imgComponent.byteBuffer, { 68 size: { height: height, width: width }, 69 srcPixelFormat: 8, 70 }) 71 } else { 72 // stride与width不一致。 73 const dstBufferSize = width * height * 1.5 74 const dstArr = new Uint8Array(dstBufferSize) 75 for (let j = 0; j < height * 1.5; j++) { 76 const srcBuf = new Uint8Array(imgComponent.byteBuffer, j * stride, width) 77 dstArr.set(srcBuf, j * width) 78 } 79 let pixelMap = await image.createPixelMap(dstArr.buffer, { 80 size: { height: height, width: width }, 81 srcPixelFormat: 8, 82 }) 83 } 84 } else { 85 console.error('byteBuffer is null'); 86 } 87 // 确保当前buffer没有在使用的情况下,可进行资源释放。 88 // 如果对buffer进行异步操作,需要在异步操作结束后再释放该资源(nextImage.release())。 89 nextImage.release(); 90 }) 91 }) 92 }) 93 } 94 ``` 95 96 97 通过 [image.Component](../../reference/apis-image-kit/arkts-apis-image-i.md#component9) 解析图片buffer数据参考: 98 99 > **注意:** 100 > 需要确认图像的宽width是否与行距rowStride一致,如果不一致可参考以下方式处理: 101 102 方式一:去除imgComponent.byteBuffer中stride数据,拷贝得到新的buffer,调用不支持stride的接口处理buffer。 103 104 ```ts 105 // 以NV21为例(YUV_420_SP格式的图片)YUV_420_SP内存计算公式:长x宽+(长x宽)/2。 106 const dstBufferSize = width * height * 1.5; 107 const dstArr = new Uint8Array(dstBufferSize); 108 // 逐行读取buffer数据。 109 for (let j = 0; j < height * 1.5; j++) { 110 // imgComponent.byteBuffer的每行数据拷贝前width个字节到dstArr中。 111 const srcBuf = new Uint8Array(imgComponent.byteBuffer, j * stride, width); 112 dstArr.set(srcBuf, j * width); 113 } 114 let pixelMap = await image.createPixelMap(dstArr.buffer, { 115 size: { height: height, width: width }, srcPixelFormat: 8 116 }); 117 ``` 118 119 方式二:根据stride*height创建pixelMap,然后调用pixelMap的cropSync方法裁剪掉多余的像素。 120 121 ```ts 122 // 创建pixelMap,width宽传行距stride的值。 123 let pixelMap = await image.createPixelMap(imgComponent.byteBuffer, { 124 size:{height: height, width: stride}, srcPixelFormat: 8}); 125 // 裁剪多余的像素。 126 pixelMap.cropSync({size:{width:width, height:height}, x:0, y:0}); 127 ``` 128 129 方式三:将原始imgComponent.byteBuffer和stride信息一起传给支持stride的接口处理。