HarmonyOS 鸿蒙Next 相机native方案中OH_ImageReceiver接收第二路预览流,如何对ImageReceiver接收到数据进行处理

发布于 1周前 作者 yuanlaile 最后一次编辑是 5天前 来自 鸿蒙OS

HarmonyOS 鸿蒙Next 相机native方案中OH_ImageReceiver接收第二路预览流,如何对ImageReceiver接收到数据进行处理
使用native相机实现双路预览,一路给xcompont的预览,一路给imageReceiver。目前需要实现跟着手机横竖屏对图像进行旋转。xcompont的预览旋转已经实现。但是imageReceiver不知道如何实现。
双路预览参考:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/native-camera-preview-imagereceiver-V5
尝试的方案:
通过OH_PixelmapNative_CreatePixelmap将数据转换为pixelmap,然后OH_PixelmapNative_Rotate函数对pixelmap进行旋转,最后通过OH_PixelmapNative_ReadPixels读取pixelmap的buffer数据。但是该方案出来的数据好像不太对,并且经常会出现OH_PixelmapNative_CreatePixelmap崩溃。
例子代码如下,哪位大神帮忙看看方案是否可行,最好是能有最佳实践案例

void VCVideoCapture::OnImageReceiverCallback(OH_ImageReceiverNative *receiver) {
    // 注意捕获错误码处理异常及对象判空,当前示例仅展示调用流程
    OH_ImageNative *image = nullptr;
    // 从bufferQueue中获取图像
    Image_ErrorCode ret = OH_ImageReceiverNative_ReadNextImage(receiver, &image);
    if (ret != IMAGE_SUCCESS) {
        OH_LOG_ERROR(LOG_APP, "[%{public}p] OH_ImageReceiverNative_ReadNextImage fail. ret is %{public}d", receiver,
                     ret);
    }
    // 读取图像宽高
    Image_Size size;
    ret = OH_ImageNative_GetImageSize(image, &size);
    if (ret != IMAGE_SUCCESS) {
        OH_LOG_ERROR(LOG_APP, "[%{public}p] OH_ImageNative_GetImageSize fail. ret is %{public}d", receiver, ret);
    }
    // 获取图像ComponentType
    size_t typeSize = 0;
    ret = OH_ImageNative_GetComponentTypes(image, nullptr, &typeSize);
    if (ret != IMAGE_SUCCESS) {
        OH_LOG_ERROR(LOG_APP, "[%{public}p] OH_ImageNative_GetComponentTypes fail. ret is %{public}d", receiver, ret);
    }
    uint32_t *types = new uint32_t[typeSize];
    ret = OH_ImageNative_GetComponentTypes(image, &types, &typeSize);
    if (ret != IMAGE_SUCCESS) {
        OH_LOG_ERROR(LOG_APP, "[%{public}p] OH_ImageNative_GetComponentTypes fail. ret is %{public}d", receiver, ret);
    }
    uint32_t component = types[0];
    // 获取图像buffer
    OH_NativeBuffer *imageBuffer = nullptr;
    ret = OH_ImageNative_GetByteBuffer(image, component, &imageBuffer);
    if (ret != IMAGE_SUCCESS) {
        OH_LOG_ERROR(LOG_APP, "[%{public}p] OH_ImageNative_GetByteBuffer fail. ret is %{public}d", receiver, ret);
    }
    size_t bufferSize = 0;
    ret = OH_ImageNative_GetBufferSize(image, component, &bufferSize);
    if (ret != IMAGE_SUCCESS) {
        OH_LOG_ERROR(LOG_APP, "[%{public}p] OH_ImageNative_GetBufferSize fail. ret is %{public}d", receiver, ret);
    }
    int32_t pixelStride = 0;
    OH_ImageNative_GetPixelStride(image, component, &pixelStride);
    if (ret != IMAGE_SUCCESS) {
        OH_LOG_ERROR(LOG_APP, "[%{public}p] OH_ImageNative_GetPixelStride fail. ret is %{public}d", receiver, ret);
    }
    // 获取图像行距
    int32_t stride = 0;
    ret = OH_ImageNative_GetRowStride(image, component, &stride);
    if (ret != IMAGE_SUCCESS) {
        OH_LOG_ERROR(LOG_APP, "[%{public}p] OH_ImageNative_GetRowStride fail. ret is %{public}d", receiver, ret);
    }

    void *srcVir = nullptr;
    OH_NativeBuffer_Map(imageBuffer, &srcVir);
    uint8_t *srcBuffer = static_cast<uint8_t *>(srcVir);
    OH_LOG_INFO(LOG_APP,
                "[%{public}p] OnImageReceiverCallback, imageWidth[%{public}d], imageHeight[%{public}d], "
                "imageBufferSize[%{public}lu], RowStride[%{public}d], pixelStride[%{public}d], bufferVir[%{public}p]",
                receiver, size.width, size.height, bufferSize, stride, pixelStride, srcBuffer);
    // 判断行距与预览流宽是否一致,如不一致,需要考虑stride对读取buffer的影响
    if (stride == size.width) {
        
        //不做旋转直接送编码(ok,数据正常)
//         size_t dstBufferSize = size.width * size.height * 1.5; // 相机预览流返回NV21格式
//         std::unique_ptr<uint8_t[]> dstBuffer = std::make_unique<uint8_t[]>(dstBufferSize);
//         uint8_t *dstPtr = dstBuffer.get();
//         memcpy(dstPtr, srcBuffer, dstBufferSize);
//         VCFrameBuffer frameBuffer;
//         frameBuffer.size = dstBufferSize;
//         frameBuffer.data = dstPtr;
//         frameBuffer.info.width = size.width;
//         frameBuffer.info.height = size.height;
//         for (IVCDataCallback *filter : gVideoCapture->mTargetFilters) {
//             filter->CallbackData(VCBufferTypeBytes, &frameBuffer);
//         }
        
        //使用PixelmapNative旋转
        size_t dstBufferSize = size.width * size.height * 1.5; // 相机预览流返回NV21格式
        std::unique_ptr<uint8_t[]> dstBuffer = std::make_unique<uint8_t[]>(dstBufferSize);
        uint8_t *dstPtr = dstBuffer.get();
        memcpy(dstPtr, srcBuffer, dstBufferSize);
        OH_Pixelmap_InitializationOptions *createOpts;
        Image_ErrorCode ret = OH_PixelmapInitializationOptions_Create(&createOpts);
        if (ret != IMAGE_SUCCESS) {
            OH_LOG_ERROR(LOG_APP, "[%{public}p] OH_PixelmapInitializationOptions_Create fail. ret is %{public}d",
                         receiver, ret);
        }
        OH_PixelmapInitializationOptions_SetWidth(createOpts, size.width);
        OH_PixelmapInitializationOptions_SetHeight(createOpts, size.height);
//         OH_PixelmapInitializationOptions_SetRowStride(createOpts, stride);
        OH_PixelmapInitializationOptions_SetPixelFormat(createOpts, PIXEL_FORMAT_NV21);
        OH_PixelmapInitializationOptions_SetAlphaType(createOpts, PIXELMAP_ALPHA_TYPE_UNKNOWN);
        OH_PixelmapNative *pixelmap = nullptr;
        ret = OH_PixelmapNative_CreatePixelmap(dstPtr, dstBufferSize, createOpts, &pixelmap);
        if (ret != IMAGE_SUCCESS) {
            OH_LOG_ERROR(LOG_APP, "[%{public}p] OH_PixelmapNative_CreatePixelmap fail. ret is %{public}d", receiver,
                         ret);
        }
        ret = OH_PixelmapInitializationOptions_Release(createOpts);
        if (ret != IMAGE_SUCCESS) {
            OH_LOG_ERROR(LOG_APP, "[%{public}p] OH_PixelmapInitializationOptions_Release fail. ret is %{public}d",
                         receiver, ret);
        }
        ret = OH_PixelmapNative_Rotate(pixelmap, 90.0);
        if (ret != IMAGE_SUCCESS) {
            OH_LOG_ERROR(LOG_APP, "[%{public}p] OH_PixelmapNative_Rotate fail. ret is %{public}d", receiver, ret);
        }
        OH_Pixelmap_ImageInfo *imageInfo;
        OH_PixelmapImageInfo_Create(&imageInfo);
        if (ret != IMAGE_SUCCESS) {
            OH_LOG_ERROR(LOG_APP, "[%{public}p] OH_PixelmapImageInfo_Create fail. ret is %{public}d", receiver, ret);
        }
        ret = OH_PixelmapNative_GetImageInfo(pixelmap, imageInfo);
        if (ret != IMAGE_SUCCESS) {
            OH_LOG_ERROR(LOG_APP, "[%{public}p] OH_PixelmapNative_GetImageInfo fail. ret is %{public}d", receiver, ret);
        }
        uint32_t width, height, rowStride;
        int32_t pixelFormat, alphaType;
        OH_PixelmapImageInfo_GetWidth(imageInfo, &width);
        OH_PixelmapImageInfo_GetHeight(imageInfo, &height);
        OH_PixelmapImageInfo_GetRowStride(imageInfo, &rowStride);
        OH_PixelmapImageInfo_GetPixelFormat(imageInfo, &pixelFormat);
        OH_PixelmapImageInfo_GetAlphaType(imageInfo, &alphaType);
        OH_PixelmapImageInfo_Release(imageInfo);

        size_t newBufferSize = rowStride * height * 1.5;
        std::unique_ptr<uint8_t[]> dstBuffer2 = std::make_unique<uint8_t[]>(newBufferSize);
        uint8_t *dstPtr2 = dstBuffer2.get();
        ret = OH_PixelmapNative_ReadPixels(pixelmap, dstPtr2, &newBufferSize);
        if (ret != IMAGE_SUCCESS) {
            OH_LOG_ERROR(LOG_APP, "[%{public}p] OH_PixelmapNative_ReadPixels fail. ret is %{public}d", receiver, ret);
        }
        ret = OH_PixelmapNative_Release(pixelmap);
        if (ret != IMAGE_SUCCESS) {
            OH_LOG_ERROR(LOG_APP, "[%{public}p] OH_PixelmapNative_Release fail. ret is %{public}d", receiver, ret);
        }
        OH_LOG_INFO(LOG_APP,
                "ImagePixelmapNativeCTest pixelmapTest GetImageInfo success, width: %{public}d, height: "
                "%{public}d, rowStride: %{public}d, pixelFormat: %{public}d, alphaType: %{public}d, newBufferSize: %{public}d",
                width, height, rowStride, pixelFormat, alphaType, newBufferSize);
        std::unique_ptr<uint8_t[]> dstBuffer3 = std::make_unique<uint8_t[]>(dstBufferSize);
        uint8_t *dstPtr3 = dstBuffer3.get();
        for (int j = 0; j < height * 1.5; j++) {
            memcpy(dstPtr3, dstPtr2, width);
            dstPtr3 += width;
            dstPtr2 += rowStride;
        }
        VCFrameBuffer frameBuffer;
        frameBuffer.size = dstBufferSize;
        frameBuffer.data = dstPtr3;
        frameBuffer.info.width = width;
        frameBuffer.info.height = height;
        for (IVCDataCallback *filter : gVideoCapture->mTargetFilters) {
            filter->CallbackData(VCBufferTypeBytes, &frameBuffer);
        }

    } else {
        // 传给其他支持stride的接口处理,或去除stride数据
        // 去除stride数据示例:将byteBuffer中的数据去除stride,拷贝得到新的dstBuffer数据
        // 传给其他不需要stride的接口处理
    }
    // 释放资源
    OH_NativeBuffer_Unmap(imageBuffer); // 释放buffer,保证bufferQueue正常轮转
    ret = OH_ImageNative_Release(image);
    if (ret != IMAGE_SUCCESS) {
        OH_LOG_ERROR(LOG_APP, "[%{public}p] OH_ImageNative_Release fail. ret is %{public}d", receiver, ret);
    }
    delete[] types;
}

更多关于HarmonyOS 鸿蒙Next 相机native方案中OH_ImageReceiver接收第二路预览流,如何对ImageReceiver接收到数据进行处理的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html

4 回复
通过libyuv的方式以已经对yuv数据旋转成功了,通过保存文件查看数据是对的。
但是又出了新的问题:旋转后数据经过编码器编码后数据还是不对。
1.开始原始yuv是1280x720,nv21,使用编码器配置1280x720,nv21格式去编码,编码后的数据正常;
2.经过旋转90度后yuv变为720x1280,nv21,编码器reset后新配置为720x1280,nv21格式,编码后数据就异常了。感觉还是数据格式上不对。

下面是旋转的代码
size_t width = size.width;
        size_t height = size.height;
        size_t dstBufferSize = width * height * 1.5; // 相机预览流返回NV21格式
        std::unique_ptr<uint8_t[]> nv21Buffer = std::make_unique<uint8_t[]>(dstBufferSize);
        uint8_t *nv21Ptr = nv21Buffer.get();
        memcpy(nv21Ptr, srcBuffer, dstBufferSize);
        std::string path_src = VCLog::GetInstance()->GetLogPath() + "/" + "test_src.yuv";
        std::ofstream outFile_src(path_src, std::ios::binary);
        outFile_src.write(reinterpret_cast<const char*>(nv21Ptr), dstBufferSize);
        outFile_src.close();
    <span class="hljs-keyword">int</span> y_size = width * height;
    <span class="hljs-keyword">int</span> uv_size = y_size / <span class="hljs-number">2</span>;
    <span class="hljs-keyword">int</span> u_size = y_size / <span class="hljs-number">4</span>;
    <span class="hljs-keyword">int</span> v_size = y_size / <span class="hljs-number">4</span>;
    uint8_t *y_src_plane = <span class="hljs-keyword">const_cast</span>&lt;uint8_t *&gt;(nv21Ptr);
    uint8_t *uv_src_plane = <span class="hljs-keyword">const_cast</span>&lt;uint8_t *&gt;(nv21Ptr) + y_size;

    <span class="hljs-built_in">std</span>::unique_ptr&lt;uint8_t[]&gt; src2Buffer = <span class="hljs-built_in">std</span>::make_unique&lt;uint8_t[]&gt;(dstBufferSize);
    uint8_t *src2Ptr = src2Buffer.get();
    uint8_t *y_src2_plane = <span class="hljs-keyword">const_cast</span>&lt;uint8_t *&gt;(src2Ptr);
    uint8_t *uv_src2_plane = <span class="hljs-keyword">const_cast</span>&lt;uint8_t *&gt;(src2Ptr) + y_size;

    <span class="hljs-built_in">std</span>::unique_ptr&lt;uint8_t[]&gt; dstBuffer = <span class="hljs-built_in">std</span>::make_unique&lt;uint8_t[]&gt;(dstBufferSize);
    uint8_t *dstPtr = dstBuffer.get();
    uint8_t *y_dist_plane = <span class="hljs-keyword">const_cast</span>&lt;uint8_t *&gt;(dstPtr);
    uint8_t *uv_dist_plane = <span class="hljs-keyword">const_cast</span>&lt;uint8_t *&gt;(dstPtr) + y_size;
    uint8_t *u_dist_plane = <span class="hljs-keyword">const_cast</span>&lt;uint8_t *&gt;(dstPtr) + y_size;
    uint8_t *v_dist_plane = <span class="hljs-keyword">const_cast</span>&lt;uint8_t *&gt;(dstPtr) + y_size + u_size;

    <span class="hljs-comment">//方法1:直接用时颜色还是有色差毕竟nv21和nv12还是有差别的</span>

// int ret2 = libyuv::NV12ToI420Rotate(y_src_plane, width, uv_src_plane, width, y_dist_plane, height, u_dist_plane, // height/2, v_dist_plane, height/2, width, height, libyuv::kRotate90); //方法2:先将nv21转nv12,然后在旋转 libyuv::NV21ToNV12(y_src_plane, width, uv_src_plane, width, y_src2_plane, width, uv_src2_plane, width, width, height); int ret2 = libyuv::NV12ToI420Rotate(y_src2_plane, width, uv_src2_plane, width, y_dist_plane, height, u_dist_plane, height/2, v_dist_plane, height/2, width, height, libyuv::kRotate90); libyuv::I420ToNV21(y_dist_plane, height, u_dist_plane, height/2, v_dist_plane, height/2, y_src2_plane, height, uv_src2_plane, height, height, width); //方法3: 对各自平面旋转,y平面OK,uv平面一直是有问题的 // libyuv::RotatePlane(y_src_plane, width, y_dist_plane, height, width, height, libyuv::kRotate90); // libyuv::RotatePlane(uv_src_plane, width, uv_dist_plane, height/2, width, height/2, libyuv::kRotate90);

    <span class="hljs-built_in">std</span>::<span class="hljs-built_in">string</span> path = VCLog::GetInstance()-&gt;GetLogPath() + <span class="hljs-string">"/"</span> + <span class="hljs-string">"test_dst.yuv"</span>;
    <span class="hljs-built_in">std</span>::ofstream outFile(path, <span class="hljs-built_in">std</span>::ios::binary);
    outFile.write(<span class="hljs-keyword">reinterpret_cast</span>&lt;<span class="hljs-keyword">const</span> <span class="hljs-keyword">char</span>*&gt;(src2Ptr), dstBufferSize);
    outFile.close();

    VCFrameBuffer frameBuffer;
    frameBuffer.size = dstBufferSize;
    frameBuffer.data = src2Ptr;
    frameBuffer.info.width = height;
    frameBuffer.info.height = width;
    <span class="hljs-keyword">for</span> (IVCDataCallback *filter : gVideoCapture-&gt;mTargetFilters) {
        filter-&gt;CallbackData(VCBufferTypeBytes, &amp;frameBuffer);
    }</code><button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button></pre></div></div>

更多关于HarmonyOS 鸿蒙Next 相机native方案中OH_ImageReceiver接收第二路预览流,如何对ImageReceiver接收到数据进行处理的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


通过相机双路预览获取帧数据,arrayBuffer转pixelMap崩溃问题,可以参考下面代码:

注意创建imageSource的SourceOptions 要预览流的profile的大小一致。

receiver.on('imageArrival', () => {
receiver.readNextImage((err: BusinessError, nextImage: image.Image) => {
if (err || nextImage === undefined) {
return;
}
nextImage.getComponent(image.ComponentType.JPEG, async (err: BusinessError, imgComponent: image.Component) => {
if (err || imgComponent === undefined) {
return;
}
if (imgComponent.byteBuffer as ArrayBuffer) {

let sourceOptions : image.SourceOptions = { sourceDensity: 120, sourcePixelFormat: 8, // NV21 sourceSize: { height: previewProfilesObj3.size.height, width: previewProfilesObj3.size.width } } let imageResource=image.createImageSource(imgComponent.byteBuffer,sourceOptions) let decodingOptions: image.DecodingOptions = { editable: true, desiredPixelFormat: 3,

} this.pixma=await imageResource.createPixelMap(decodingOptions)

} else { return; } }) }) })<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>

要通过ImageReceiver获取图像并通过NativeWindow渲染显示的旋转角度,可以按照以下步骤进行:

1.获取ImageReceiver对象:通过ImageReceiverNative获取ImageReceiver对象。

2.获取ImageSourceInfo对象:使用OH_ImageSource_Info_Create函数创建一个ImageSourceInfo指针。

3.获取旋转角度:使用OH_ImageSourceInfo_GetRotate函数获取ImageSourceInfo对象中的旋转角度信息。

4.设置旋转角度:如果需要设置旋转角度,可以使用OH_ImageSourceInfo_SetRotate函数。

5.渲染图像:使用NativeWindow将图像渲染显示,并应用之前获取的旋转角度。具体的步骤如下:- 调用OH_ImageReceiverNative_GetCapacity获取ImageReceiver的容量。- 调用OH_ImageSourceInfo_GetRotate获取旋转角度。- 如果需要设置旋转角度,可以调用OH_ImageSourceInfo_SetRotate。- 使用OH_ImageSourceInfo_Release释放ImageSourceInfo指针。- 使用OH_DecodingOptions_Create创建DecodingOptions对象。- 使用OH_DecodingOptions_GetRotate获取旋转角度。- 如果需要设置旋转角度,可以调用OH_DecodingOptions_SetRotate。- 使用OH_PixelmapNative_ReadPixels读取图像像素数据。- 使用OH_PixelmapNative_Scale根据输入的宽高对图片进行缩放。- 使用OH_PixelmapNative_Rotate根据输入的角度对图片进行旋转。- 使用OH_PixelmapNative_WritePixels将处理后的图像像素数据写回PixelMap。- 使用OH_PixelmapNative_Release释放PixelmapNative指针。

通过以上步骤,可以实现通过ImageReceiver获取图像并通过NativeWindow渲染显示的旋转角度。

当前规格,预览流旋转角度固定值 90 度,

目前双路预览流角度固定前置摄像头得到的YUV数据顺时针旋转了90度,后置摄像头得到的YUV数据顺时针旋转了270度。

可以通过YUV数据进行旋转操作,对于前置摄像头的数据还需进行镜像翻转操作

NativeWindow设置的旋转角度是逆时针

附arkTS实现双录预览demo:

import { camera } from ‘@kit.CameraKit’;
import { BusinessError } from ‘@kit.BasicServicesKit’;
import { image } from ‘@kit.ImageKit’;
import { photoAccessHelper } from ‘@kit.MediaLibraryKit’;
import { abilityAccessCtrl, PermissionRequestResult, Permissions } from ‘@kit.AbilityKit’;
import { promptAction, router } from ‘@kit.ArkUI’;
import { fileIo } from ‘@kit.CoreFileKit’;

const TAG = ‘[CameraDemo]’;

@Entry @Component export struct CameraDemo { context: Context = getContext(this) as Context; @State pixelMap: image.PixelMap | undefined = undefined; @State finalPixelMap: image.PixelMap | undefined = undefined; @State buffer: ArrayBuffer | undefined = undefined; @State surfaceId: string = ‘’; @State hasPicture: boolean = false; @State fileNames: string[] = []; @State imageSize: image.Size = { width: 1920, height: 1080 }; @State saveButtonOptions: SaveButtonOptions = { icon: SaveIconStyle.FULL_FILLED, text: SaveDescription.SAVE_IMAGE, buttonType: ButtonType.Capsule } // 设置安全控件按钮属性 private mXComponentController: XComponentController = new XComponentController; private cameraManager: camera.CameraManager | undefined = undefined; private cameraSession: camera.PhotoSession | undefined = undefined; private photoOutput: camera.PhotoOutput | undefined = undefined; private cameraInput: camera.CameraInput | undefined = undefined; private previewOutput: camera.PreviewOutput | undefined = undefined; private previewOutput2: camera.PreviewOutput | undefined = undefined; private imageReceiver: image.ImageReceiver | undefined = undefined;

aboutToAppear(): void { let permissions: Array<Permissions> = [ ‘ohos.permission.CAMERA’, ‘ohos.permission.WRITE_MEDIA’, ‘ohos.permission.READ_MEDIA’, ‘ohos.permission.MEDIA_LOCATION’, ]; let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager(); // requestPermissionsFromUser会判断权限的授权状态来决定是否唤起弹窗 atManager.requestPermissionsFromUser(this.context, permissions).then((data: PermissionRequestResult) => { let grantStatus: Array<number> = data.authResults; let length: number = grantStatus.length; for (let i = 0; i < length; i++) { if (grantStatus[i] != 0) { // 用户拒绝授权,提示用户必须授权才能访问当前页面的功能,并引导用户到系统设置中打开相应的权限 return; } } console.info(${TAG} Success to request permissions from user. authResults is ${grantStatus}.); }).catch((err: BusinessError) => { console.error(${TAG} Failed to request permissions from user. Code is ${err.code}, message is ${err.message}); }) }

async onPageShow() { await this.prepareCamera(); }

createCameraManager() { // 创建CameraManager对象 let cameraManager: camera.CameraManager = camera.getCameraManager(this.context); if (!cameraManager) { console.error(‘CameraDemo camera.getCameraManager error’); return; } this.cameraManager = cameraManager;

<span class="hljs-comment">// 监听相机状态变化</span>
<span class="hljs-keyword">this</span>.cameraManager.on(<span class="hljs-string">'cameraStatus'</span>, (err: BusinessError, cameraStatusInfo: camera.CameraStatusInfo) =&gt; {
  console.info(`CameraDemo camera: ${cameraStatusInfo.camera.cameraId}, status: ${cameraStatusInfo.status}`);
});

}

build() { Column() { Flex({ direction: FlexDirection.Row, justifyContent: FlexAlign.Start }) { Image($r(‘app.media.photo_12668’)) .width(30) .onClick(() => { router.back() }) }

  Column({ space: <span class="hljs-number">20</span> }) {
    <span class="hljs-keyword">if</span> (<span class="hljs-keyword">this</span>.hasPicture) {
      Image(<span class="hljs-keyword">this</span>.finalPixelMap)
        .objectFit(ImageFit.Fill)
        .width(<span class="hljs-string">'100%'</span>)
        .height(<span class="hljs-number">300</span>)

      SaveButton(<span class="hljs-keyword">this</span>.saveButtonOptions)<span class="hljs-comment">// 创建安全控件按钮</span>
        .onClick(async (event, result: SaveButtonOnClickResult) =&gt; {
          <span class="hljs-keyword">if</span> (result == SaveButtonOnClickResult.SUCCESS) {
            <span class="hljs-keyword">if</span> (<span class="hljs-keyword">this</span>.finalPixelMap) {
              <span class="hljs-keyword">try</span> {
                <span class="hljs-comment">// 1、使用安全控件创建文件</span>
                <span class="hljs-keyword">let</span> phAccessHelper: photoAccessHelper.PhotoAccessHelper =
                  photoAccessHelper.getPhotoAccessHelper(<span class="hljs-keyword">this</span>.context);
                <span class="hljs-keyword">let</span> options: photoAccessHelper.CreateOptions = {
                  title: <span class="hljs-built_in">Date</span>.now().toString()
                };
                <span class="hljs-comment">// createAsset的调用需要ohos.permission.READ_IMAGEVIDEO和ohos.permission.WRITE_IMAGEVIDEO的权限</span>
                <span class="hljs-keyword">let</span> photoUri: string =
                  await phAccessHelper.createAsset(photoAccessHelper.PhotoType.IMAGE, <span class="hljs-string">'png'</span>, options);
                console.info(<span class="hljs-string">'CameraDemo createAsset successfully, photoUri: '</span> + photoUri);
                <span class="hljs-comment">// 2.1、方式一:通过文件管理写入文件</span>
                <span class="hljs-keyword">let</span> file: fileIo.File = fileIo.openSync(photoUri, fileIo.OpenMode.WRITE_ONLY);
                fileIo.writeSync(file.fd, <span class="hljs-keyword">this</span>.buffer);
                fileIo.closeSync(file);
                <span class="hljs-comment">// 2.2、方式二:通过imagePacker写入文件</span>
                <span class="hljs-comment">// let file: fileIo.File = fileIo.openSync(photoUri, fileIo.OpenMode.WRITE_ONLY);</span>
                <span class="hljs-comment">// let imagePacker: image.ImagePacker = image.createImagePacker();</span>
                <span class="hljs-comment">// let packOpts: image.PackingOption = { format: 'image/jpeg', quality: 100 };</span>
                <span class="hljs-comment">// await imagePacker.packToFile(this.finalPixelMap, file.fd, packOpts);</span>
                <span class="hljs-comment">// fileIo.closeSync(file);</span>
                promptAction.showToast({ message: `保存成功` })
              } <span class="hljs-keyword">catch</span> (error) {
                <span class="hljs-keyword">let</span> err = error as BusinessError;
                console.error(`CameraDemo savePicture error: ${<span class="hljs-built_in">JSON</span>.stringify(err)}`);
                promptAction.showToast({ message: `保存失败` })
              }
            }
          } <span class="hljs-keyword">else</span> {
            console.error(<span class="hljs-string">'CameraDemo SaveButtonOnClickResult createAsset failed.'</span>);
            promptAction.showToast({ message: `保存失败` })
          }
          setTimeout(() =&gt; {
            <span class="hljs-keyword">this</span>.hasPicture = <span class="hljs-literal">false</span>;
            <span class="hljs-comment">// this.finalPixelMap = undefined;</span>
          }, <span class="hljs-number">1000</span>)
        })

    } <span class="hljs-keyword">else</span> {
      XComponent({
        id: <span class="hljs-string">''</span>,
        type: <span class="hljs-string">'surface'</span>,
        libraryname: <span class="hljs-string">''</span>,
        controller: <span class="hljs-keyword">this</span>.mXComponentController
      })
        .onLoad(() =&gt; {
          <span class="hljs-comment">// 设置Surface宽高(1920*1080),预览尺寸设置参考前面 previewProfilesArray 获取的当前设备所支持的预览分辨率大小去设置</span>
          <span class="hljs-comment">// 预览流与录像输出流的分辨率的宽高比要保持一致</span>
          <span class="hljs-keyword">this</span>.mXComponentController.setXComponentSurfaceSize({
            surfaceWidth: <span class="hljs-number">1920</span> * <span class="hljs-number">1.3</span>,
            surfaceHeight: <span class="hljs-number">1080</span> * <span class="hljs-number">1.3</span>
          });
          <span class="hljs-comment">// 获取Surface ID</span>
          <span class="hljs-keyword">this</span>.surfaceId = <span class="hljs-keyword">this</span>.mXComponentController.getXComponentSurfaceId();
          setTimeout(async () =&gt; {
            await <span class="hljs-keyword">this</span>.prepareCamera();
          }, <span class="hljs-number">500</span>);
        })
        .width(<span class="hljs-string">'100%'</span>)
        .height(<span class="hljs-number">300</span>)

      <span class="hljs-comment">// 双路预览</span>
      Image(<span class="hljs-keyword">this</span>.pixelMap)
        .objectFit(ImageFit.Fill)
        .width(<span class="hljs-string">'100%'</span>)
        .height(<span class="hljs-number">300</span>)

      Button(<span class="hljs-string">'拍照'</span>)
        .width(<span class="hljs-number">200</span>)
        .height(<span class="hljs-number">30</span>)
        .onClick(async () =&gt; {
          <span class="hljs-keyword">let</span> photoCaptureSetting: camera.PhotoCaptureSetting = {
            quality: camera.QualityLevel.QUALITY_LEVEL_HIGH, <span class="hljs-comment">// 设置图片质量高</span>
            rotation: camera.ImageRotation.ROTATION_0, <span class="hljs-comment">// 设置图片旋转角度0</span>
            <span class="hljs-comment">// mirror: true// 镜像</span>
          }
          <span class="hljs-comment">// 1、通过拍照流实现:点击拍照</span>
          await <span class="hljs-keyword">this</span>.photoOutput?.capture(photoCaptureSetting).catch((error: BusinessError) =&gt; {
            console.error(`CameraDemo Failed to capture the photo ${error.message}`); <span class="hljs-comment">//不符合条件则进入</span>
          })
          <span class="hljs-comment">// 2、从imageReceiver中获取图片,实现拍照</span>
          <span class="hljs-comment">// this.finalPixelMap = this.pixelMap;</span>
          <span class="hljs-keyword">this</span>.hasPicture = <span class="hljs-literal">true</span>;
        })
    }
  }
  .width(<span class="hljs-string">'100%'</span>)
  .height(<span class="hljs-string">'100%'</span>)
  .padding(<span class="hljs-number">15</span>)
  .borderRadius(<span class="hljs-number">8</span>)
}
.width(<span class="hljs-string">'100%'</span>)
.height(<span class="hljs-string">'100%'</span>)
.backgroundColor(<span class="hljs-string">'#FFFFFF'</span>)

}

async prepareCamera() { this.createCameraManager();

<span class="hljs-keyword">if</span> (!<span class="hljs-keyword">this</span>.cameraManager) {
  console.error(<span class="hljs-string">'CameraDemo cameraManager is undefined.'</span>)
  <span class="hljs-keyword">return</span>;
}

<span class="hljs-comment">// 获取支持指定的相机设备对象</span>
<span class="hljs-keyword">let</span> cameraDevices: <span class="hljs-built_in">Array</span>&lt;camera.CameraDevice&gt; = [];
<span class="hljs-keyword">try</span> {
  cameraDevices = <span class="hljs-keyword">this</span>.cameraManager.getSupportedCameras();
} <span class="hljs-keyword">catch</span> (error) {
  <span class="hljs-keyword">let</span> err = error as BusinessError;
  console.error(`CameraDemo The getSupportedCameras call failed. error: ${<span class="hljs-built_in">JSON</span>.stringify(err)}`)
}

cameraDevices.forEach((cameraDevice: camera.CameraDevice) =&gt; {
  console.info(`CameraDemo cameraId: ${cameraDevice.cameraId}, cameraPosition: ${cameraDevice.cameraPosition.toString()}, cameraType: ${cameraDevice.cameraType.toString()}, connectionType: ${cameraDevice.connectionType.toString()}`)
})

<span class="hljs-comment">// 创建相机输入流</span>
<span class="hljs-keyword">try</span> {
  <span class="hljs-keyword">this</span>.cameraInput = <span class="hljs-keyword">this</span>.cameraManager.createCameraInput(cameraDevices[<span class="hljs-number">0</span>]);
} <span class="hljs-keyword">catch</span> (error) {
  <span class="hljs-keyword">let</span> err = error as BusinessError;
  console.error(`CameraDemo createCaptureSession error. error: ${<span class="hljs-built_in">JSON</span>.stringify(err)}`);
  <span class="hljs-keyword">return</span>
}
<span class="hljs-comment">// 监听cameraInput错误信息</span>
<span class="hljs-keyword">this</span>.cameraInput.on(<span class="hljs-string">'error'</span>, cameraDevices[<span class="hljs-number">0</span>], (error: BusinessError) =&gt; {
  console.error(`CameraDemo Camera input error: ${<span class="hljs-built_in">JSON</span>.stringify(error)}`);
});

<span class="hljs-comment">// 打开相机</span>
<span class="hljs-keyword">try</span> {
  await <span class="hljs-keyword">this</span>.cameraInput.open();
} <span class="hljs-keyword">catch</span> (error) {
  <span class="hljs-keyword">let</span> err = error as BusinessError;
  console.error(`CameraDemo cameraInput open error. error: ${<span class="hljs-built_in">JSON</span>.stringify(err)}`);
}

<span class="hljs-comment">// 获取指定的相机设备对象支持的模式</span>
<span class="hljs-keyword">let</span> cameraSceneModes: <span class="hljs-built_in">Array</span>&lt;camera.SceneMode&gt; = [];
<span class="hljs-keyword">try</span> {
  cameraSceneModes = <span class="hljs-keyword">this</span>.cameraManager.getSupportedSceneModes(cameraDevices[<span class="hljs-number">0</span>]);
  cameraSceneModes.forEach((cameraSceneMode: camera.SceneMode) =&gt; {
    console.info(`CameraDemo cameraSceneMode: ${cameraSceneMode.toString()}`)
  })
} <span class="hljs-keyword">catch</span> (error) {
  <span class="hljs-keyword">let</span> err = error as BusinessError;
  console.error(`CameraDemo The getSupportedSceneModes call failed. error: ${<span class="hljs-built_in">JSON</span>.stringify(err)}`)
}

<span class="hljs-comment">// 获取相机设备支持的输出流能力</span>
<span class="hljs-keyword">let</span> cameraOutputCapability: camera.CameraOutputCapability =
  <span class="hljs-keyword">this</span>.cameraManager.getSupportedOutputCapability(cameraDevices[<span class="hljs-number">0</span>], camera.SceneMode.NORMAL_PHOTO)
<span class="hljs-keyword">if</span> (!cameraOutputCapability) {
  console.error(<span class="hljs-string">'CameraDemo cameraManager.getSupportedOutputCapability error'</span>);
  <span class="hljs-keyword">return</span>;
}
<span class="hljs-keyword">this</span>.printCameraOutputCapability(cameraOutputCapability);
<span class="hljs-keyword">let</span> previewProfile = cameraOutputCapability.previewProfiles[<span class="hljs-number">0</span>];
cameraOutputCapability.previewProfiles.forEach((profile) =&gt; {
  <span class="hljs-keyword">if</span> (profile.size.width == <span class="hljs-keyword">this</span>.imageSize.width &amp;&amp; profile.size.height == <span class="hljs-keyword">this</span>.imageSize.height) {
    previewProfile = profile;
    <span class="hljs-keyword">return</span>;
  }
})
<span class="hljs-keyword">this</span>.imageSize = previewProfile.size;

<span class="hljs-comment">// 创建相机预览输出流</span>
<span class="hljs-keyword">try</span> {
  <span class="hljs-keyword">this</span>.previewOutput = <span class="hljs-keyword">this</span>.cameraManager.createPreviewOutput(previewProfile, <span class="hljs-keyword">this</span>.surfaceId);
} <span class="hljs-keyword">catch</span> (error) {
  <span class="hljs-keyword">let</span> err = error as BusinessError;
  console.error(`CameraDemo createCaptureSession error. error: ${<span class="hljs-built_in">JSON</span>.stringify(err)}`);
  <span class="hljs-keyword">return</span>
}
<span class="hljs-comment">// 监听previewOutput错误信息</span>
<span class="hljs-keyword">this</span>.previewOutput.on(<span class="hljs-string">'error'</span>, (error: BusinessError) =&gt; {
  console.error(`CameraDemo previewOutput error: ${<span class="hljs-built_in">JSON</span>.stringify(error)}`);
});

<span class="hljs-comment">//双路预览: 创建 预览流2 输出对象</span>
<span class="hljs-keyword">try</span> {
  <span class="hljs-keyword">this</span>.imageReceiver = image.createImageReceiver(<span class="hljs-keyword">this</span>.imageSize, image.ImageFormat.JPEG, <span class="hljs-number">8</span>);
  <span class="hljs-keyword">let</span> imageReceiverSurfaceId: string = await <span class="hljs-keyword">this</span>.imageReceiver.getReceivingSurfaceId();
  <span class="hljs-keyword">this</span>.previewOutput2 = <span class="hljs-keyword">this</span>.cameraManager.createPreviewOutput(previewProfile, imageReceiverSurfaceId);
  <span class="hljs-keyword">this</span>.onImageArrival(<span class="hljs-keyword">this</span>.imageReceiver);
} <span class="hljs-keyword">catch</span> (error) {
  <span class="hljs-keyword">let</span> err = error as BusinessError;
  console.error(`CameraDemo createImageReceiver error ${<span class="hljs-built_in">JSON</span>.stringify(err)}`);
  <span class="hljs-keyword">return</span>
}
<span class="hljs-comment">// 监听previewOutput2错误信息</span>
<span class="hljs-keyword">this</span>.previewOutput2.on(<span class="hljs-string">'error'</span>, (error: BusinessError) =&gt; {
  console.error(`CameraDemo previewOutput2 error: ${<span class="hljs-built_in">JSON</span>.stringify(error)}`);
});

<span class="hljs-comment">// 创建拍照输出流</span>
<span class="hljs-keyword">try</span> {
  <span class="hljs-keyword">let</span> photoProfile = cameraOutputCapability.photoProfiles[<span class="hljs-number">0</span>];
  cameraOutputCapability.photoProfiles.forEach((profile) =&gt; {
    <span class="hljs-keyword">if</span> (profile.size.width == <span class="hljs-keyword">this</span>.imageSize.width &amp;&amp; profile.size.height == <span class="hljs-keyword">this</span>.imageSize.height) {
      photoProfile = profile;
      <span class="hljs-keyword">return</span>;
    }
  })
  <span class="hljs-keyword">this</span>.photoOutput = <span class="hljs-keyword">this</span>.cameraManager.createPhotoOutput(photoProfile);
} <span class="hljs-keyword">catch</span> (error) {
  <span class="hljs-keyword">let</span> err = error as BusinessError;
  console.error(`CameraDemo createPhotoOutput error ${<span class="hljs-built_in">JSON</span>.stringify(err)}`);
}
<span class="hljs-keyword">if</span> (<span class="hljs-keyword">this</span>.photoOutput === <span class="hljs-literal">undefined</span>) {
  console.error(<span class="hljs-string">'CameraDemo photoOutput is undefined.'</span>);
  <span class="hljs-keyword">return</span>;
}

<span class="hljs-comment">//调用上面的回调函数来保存图片</span>
<span class="hljs-keyword">this</span>.setPhotoOutputCb(<span class="hljs-keyword">this</span>.photoOutput);
<span class="hljs-comment">// 创建相机会话</span>
<span class="hljs-keyword">try</span> {
  <span class="hljs-comment">// this.cameraSession = this.cameraManager.createCaptureSession();</span>
  <span class="hljs-keyword">this</span>.cameraSession = <span class="hljs-keyword">this</span>.cameraManager.createSession&lt;camera.PhotoSession&gt;(camera.SceneMode.NORMAL_PHOTO);
} <span class="hljs-keyword">catch</span> (error) {
  <span class="hljs-keyword">let</span> err = error as BusinessError;
  console.error(`CameraDemo createCaptureSession error. error: ${<span class="hljs-built_in">JSON</span>.stringify(err)}`);
  <span class="hljs-keyword">return</span>
}
<span class="hljs-comment">// 监听session错误信息</span>
<span class="hljs-keyword">this</span>.cameraSession.on(<span class="hljs-string">'error'</span>, (error: BusinessError) =&gt; {
  console.error(`CameraDemo Capture session error: ${<span class="hljs-built_in">JSON</span>.stringify(error)}`);
});

<span class="hljs-comment">// 开始会话配置</span>
<span class="hljs-keyword">try</span> {
  <span class="hljs-keyword">this</span>.cameraSession.beginConfig()
} <span class="hljs-keyword">catch</span> (error) {
  <span class="hljs-keyword">let</span> err = error as BusinessError;
  console.error(`CameraDemo beginConfig error. error: ${<span class="hljs-built_in">JSON</span>.stringify(err)}`);
}

<span class="hljs-comment">// 向会话中添加相机输入流</span>
<span class="hljs-keyword">try</span> {
  <span class="hljs-keyword">this</span>.cameraSession.addInput(<span class="hljs-keyword">this</span>.cameraInput)
} <span class="hljs-keyword">catch</span> (error) {
  <span class="hljs-keyword">let</span> err = error as BusinessError;
  console.error(`CameraDemo addInput error. error: ${<span class="hljs-built_in">JSON</span>.stringify(err)}`);
}

<span class="hljs-comment">// 向会话中添加预览输出流</span>
<span class="hljs-keyword">try</span> {
  <span class="hljs-keyword">this</span>.cameraSession.addOutput(<span class="hljs-keyword">this</span>.previewOutput)
} <span class="hljs-keyword">catch</span> (error) {
  <span class="hljs-keyword">let</span> err = error as BusinessError;
  console.error(`CameraDemo add previewOutput error. error: ${<span class="hljs-built_in">JSON</span>.stringify(err)}`);
}

<span class="hljs-comment">// 向会话中添加预览输出流2</span>
<span class="hljs-keyword">try</span> {
  <span class="hljs-keyword">this</span>.cameraSession.addOutput(<span class="hljs-keyword">this</span>.previewOutput2)
} <span class="hljs-keyword">catch</span> (error) {
  <span class="hljs-keyword">let</span> err = error as BusinessError;
  console.error(`CameraDemo add previewOutput2 error. error: ${<span class="hljs-built_in">JSON</span>.stringify(err)}`);
}

<span class="hljs-comment">// 向会话中添加拍照输出流</span>
<span class="hljs-keyword">try</span> {
  <span class="hljs-keyword">this</span>.cameraSession.addOutput(<span class="hljs-keyword">this</span>.photoOutput);
} <span class="hljs-keyword">catch</span> (error) {
  <span class="hljs-keyword">let</span> err = error as BusinessError;
  console.error(`CameraDemo add photoOutput error. error: ${<span class="hljs-built_in">JSON</span>.stringify(err)}`);
}

<span class="hljs-comment">// 提交会话配置</span>
<span class="hljs-keyword">try</span> {
  await <span class="hljs-keyword">this</span>.cameraSession.commitConfig();
} <span class="hljs-keyword">catch</span> (error) {
  <span class="hljs-keyword">let</span> err = error as BusinessError;
  console.error(`CameraDemo captureSession commitConfig error: ${<span class="hljs-built_in">JSON</span>.stringify(err)}`);
}

<span class="hljs-comment">// 启动会话</span>
<span class="hljs-keyword">try</span> {
  await <span class="hljs-keyword">this</span>.cameraSession.start();
} <span class="hljs-keyword">catch</span> (error) {
  <span class="hljs-keyword">let</span> err = error as BusinessError;
  console.error(`CameraDemo captureSession start error: ${<span class="hljs-built_in">JSON</span>.stringify(err)}`);
}

<span class="hljs-comment">// 配置相机的参数可以调整拍照的一些功能,包括闪光灯、变焦、焦距等。</span>
<span class="hljs-keyword">this</span>.configuringSession(<span class="hljs-keyword">this</span>.cameraSession)

}

configuringSession(photoSession: camera.PhotoSession): void { // 判断设备是否支持闪光灯 let flashStatus: boolean = false; try { flashStatus = photoSession.hasFlash(); } catch (error) { let err = error as BusinessError; console.error(CameraDemo Failed to hasFlash. error: ${<span class="hljs-built_in">JSON</span>.stringify(err)}); } console.info(CameraDemo Returned <span class="hljs-keyword">with</span> the flash light support status: ${flashStatus}); if (flashStatus) { // 判断是否支持自动闪光灯模式 let flashModeStatus: boolean = false; try { let status: boolean = photoSession.isFlashModeSupported(camera.FlashMode.FLASH_MODE_AUTO); flashModeStatus = status; } catch (error) { let err = error as BusinessError; console.error(CameraDemo Failed to check whether the flash mode is supported. error: ${<span class="hljs-built_in">JSON</span>.stringify(err)}); } if (flashModeStatus) { // 设置自动闪光灯模式 try { photoSession.setFlashMode(camera.FlashMode.FLASH_MODE_AUTO); } catch (error) { let err = error as BusinessError; console.error(CameraDemo Failed to set the flash mode. error: ${<span class="hljs-built_in">JSON</span>.stringify(err)}); } } } // 判断是否支持连续自动变焦模式 let focusModeStatus: boolean = false; try { let status: boolean = photoSession.isFocusModeSupported(camera.FocusMode.FOCUS_MODE_CONTINUOUS_AUTO); focusModeStatus = status; } catch (error) { let err = error as BusinessError; console.error(CameraDemo Failed to check whether the focus mode is supported. error: ${<span class="hljs-built_in">JSON</span>.stringify(err)}); } if (focusModeStatus) { // 设置连续自动变焦模式 try { photoSession.setFocusMode(camera.FocusMode.FOCUS_MODE_CONTINUOUS_AUTO); } catch (error) { let err = error as BusinessError; console.error(CameraDemo Failed to set the focus mode. error: ${<span class="hljs-built_in">JSON</span>.stringify(err)}); } } // 获取相机支持的可变焦距比范围 let zoomRatioRange: Array<number> = []; try { zoomRatioRange = photoSession.getZoomRatioRange(); } catch (error) { let err = error as BusinessError; console.error(CameraDemo Failed to get the zoom ratio range. error: ${<span class="hljs-built_in">JSON</span>.stringify(err)}); } if (zoomRatioRange.length <= 0) { return; } // 设置可变焦距比 try { photoSession.setZoomRatio(zoomRatioRange[0]); } catch (error) { let err = error as BusinessError; console.error(CameraDemo Failed to set the zoom ratio value. error: ${<span class="hljs-built_in">JSON</span>.stringify(err)}); } }

// 通过Surface进行数据传递,通过ImageReceiver的surface获取预览图像。 async onImageArrival(receiver: image.ImageReceiver): Promise<void> { receiver.on(‘imageArrival’, () => { receiver.readNextImage(async (err, nextImage: image.Image) => { console.info(enter CameraDemo imageArrival, nextImage: ${<span class="hljs-built_in">JSON</span>.stringify(nextImage)}) if (err || nextImage === undefined) { console.error(CameraDemo imageArrival error, error is ${<span class="hljs-built_in">JSON</span>.stringify(err)} or nextImage is <span class="hljs-literal">undefined</span>) return; } nextImage.getComponent(image.ComponentType.JPEG, async (err, imgComponent: image.Component) => { if (err || imgComponent === undefined) { console.error(CameraDemo getComponent error, error is ${<span class="hljs-built_in">JSON</span>.stringify(err)} or imgComponent is <span class="hljs-literal">undefined</span>) return; } if (imgComponent.byteBuffer as ArrayBuffer) { let sourceOptions: image.SourceOptions = { sourceDensity: 0, sourcePixelFormat: image.PixelMapFormat.NV21, // NV21 sourceSize: this.imageSize } let imageSource: image.ImageSource = image.createImageSource(imgComponent.byteBuffer, sourceOptions); let opts: image.InitializationOptions = { editable: false, pixelFormat: image.PixelMapFormat.NV21, size: this.imageSize } let pixelMap = await imageSource.createPixelMap(opts); await pixelMap.rotate(90.0); this.pixelMap = pixelMap; await imageSource.release(); } else { return; } nextImage.release() }) }) }) }

setPhotoOutputCb(photoOutput: camera.PhotoOutput) { //设置回调之后,调用photoOutput的capture方法,就会将拍照的buffer回传到回调中 photoOutput.on(‘photoAvailable’, (errCode: BusinessError, photo: camera.Photo): void => { console.info(CameraDemo getPhoto start. err: ${<span class="hljs-built_in">JSON</span>.stringify(errCode)}); if (errCode || photo === undefined || photo.main === undefined) { console.error(‘CameraDemo getPhoto failed’); return; } let imageObj = photo.main; imageObj.getComponent(image.ComponentType.JPEG, async (errCode: BusinessError, component: image.Component): Promise<void> => { console.info(‘CameraDemo getComponent start’); if (errCode || component === undefined) { console.error(‘CameraDemo getComponent failed’); return; } let buffer: ArrayBuffer; if (component.byteBuffer) { buffer = component.byteBuffer; this.buffer = buffer; let sourceOptions: image.SourceOptions = { sourceDensity: 0, // 在不确定当前密度时传0 sourcePixelFormat: image.PixelMapFormat.RGBA_8888, sourceSize: this.imageSize } let imageSource: image.ImageSource = image.createImageSource(buffer, sourceOptions); let opts: image.InitializationOptions = { editable: false, pixelFormat: image.PixelMapFormat.RGBA_8888, size: this.imageSize } let pixelMap = await imageSource.createPixelMap(opts); this.finalPixelMap = pixelMap; } else { console.error(‘CameraDemo byteBuffer is null’); return; } }); }); }

async copyPixelMap(imagePixel: PixelMap): Promise<image.PixelMap> { let imageInfo: image.ImageInfo = await imagePixel.getImageInfo(); console.info(copyPixelMapSize: width:${imageInfo?.size.width} height:${imageInfo?.size.height}); let newRegion: image.Region = { size: { height: imageInfo.size.height, width: imageInfo.size.width }, x: 0, y: 0 } let newArea: image.PositionArea = { pixels: new ArrayBuffer(imageInfo.size.height * imageInfo.size.width * 4), offset: 0, stride: imageInfo.stride, region: newRegion } await imagePixel.readPixels(newArea); let opts: image.InitializationOptions = { editable: true, pixelFormat: 4, size: imageInfo.size }; let imagePixelCache = await image.createPixelMap(newArea.pixels, opts); return imagePixelCache; }

printCameraOutputCapability(cameraOutputCapability: camera.CameraOutputCapability) { let previewProfileArr: Array<camera.Profile> = cameraOutputCapability.previewProfiles; let photoProfileArr: Array<camera.Profile> = cameraOutputCapability.photoProfiles; let videoProfileArr: Array<camera.VideoProfile> = cameraOutputCapability.videoProfiles; let supportedMetadataObjectTypeArr: Array<camera.MetadataObjectType> = cameraOutputCapability.supportedMetadataObjectTypes; previewProfileArr.forEach((value: camera.Profile, index: number) => { console.info(CameraDemo 支持的预览尺寸: [${value.size.width},${value.size.height}]); }) photoProfileArr.forEach((value: camera.Profile, index: number) => { console.info(CameraDemo 支持的拍照尺寸: [${value.size.width},${value.size.height}]); }) videoProfileArr.forEach((value: camera.VideoProfile, index: number) => { console.info(CameraDemo 支持的录像尺寸: [${value.size.width},${value.size.height}], 支持的帧率范围: [${value.frameRateRange.min},${value.frameRateRange.max}]); }) supportedMetadataObjectTypeArr.forEach((value: camera.MetadataObjectType, index: number) => { console.info(CameraDemo 支持的metadata流类型: ${value}); }) } }<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>

let sourceOptions : image.SourceOptions = { sourceDensity: 120, sourcePixelFormat: 8, // NV21 sourceSize: { height: previewProfilesObj3.size.height, width: previewProfilesObj3.size.width } } let imageResource=image.createImageSource(imgComponent.byteBuffer,sourceOptions) let decodingOptions: image.DecodingOptions = { editable: true, desiredPixelFormat: 3,

} this.pixma=await imageResource.createPixelMap(decodingOptions)

该段arkts的代码,我当时也尝试过用以下代码实现,好像也是不行,不知道是哪里配错了,特别是创建OH_ImageSourceNative时没有像arkts有配置SourceOptions的接口。后来我也尝试取数据直接用libyuv去转换,也是有些问题:

    size_t dstBufferSize = size.width * size.height * 1.5; // 相机预览流返回NV21格式
    std::unique_ptr&lt;uint8_t[]&gt; dstBuffer = std::make_unique&lt;uint8_t[]&gt;(dstBufferSize);
    uint8_t *dstPtr = dstBuffer.get();
    memcpy(dstPtr, srcBuffer, dstBufferSize);
    OH_ImageSourceNative *imageSource;
    ret = OH_ImageSourceNative_CreateFromData(dstPtr, dstBufferSize, &amp;imageSource);
    if (ret != IMAGE_SUCCESS) {
        OH_LOG_ERROR(LOG_APP, "[%{public}p] OH_ImageSourceNative_CreateFromData fail. ret is %{public}d",
                receiver,
                     ret);
    }
    OH_PixelmapNative *pixelmap = nullptr;
    OH_DecodingOptions *decodingOptions;
    ret = OH_DecodingOptions_Create(&amp;decodingOptions);
    ret = OH_DecodingOptions_SetPixelFormat(decodingOptions, PIXEL_FORMAT_NV21);
    ret = OH_ImageSourceNative_CreatePixelmap(imageSource, decodingOptions, &amp;pixelmap);
    if (ret != IMAGE_SUCCESS) {
        OH_LOG_ERROR(LOG_APP, "[%{public}p] OH_ImageSourceNative_CreatePixelmap fail. ret is %{public}d",
                receiver,
                     ret);
    }
            ret = OH_DecodingOptions_Release(decodingOptions);  
    if (ret != IMAGE_SUCCESS) {
        OH_LOG_ERROR(LOG_APP, "[%{public}p] OH_DecodingOptions_Release fail. ret is %{public}d",
                receiver,
                     ret);
    }

在HarmonyOS鸿蒙Next相机native方案中,处理OH_ImageReceiver接收的第二路预览流数据,通常涉及以下几个步骤:

  1. 初始化ImageReceiver:确保OH_ImageReceiver已正确初始化,并配置为接收第二路预览流。

  2. 数据回调处理:在OH_ImageReceiver的回调函数中处理接收到的图像数据。这通常包括从回调参数中提取图像数据。

  3. 数据格式转换:根据需求,将接收到的图像数据转换为适合后续处理的格式。例如,从YUV转换为RGB或进行分辨率调整。

  4. 图像处理:对转换后的图像数据进行处理,如滤镜应用、图像增强、目标检测等。

  5. 显示或存储:将处理后的图像数据发送到显示设备或保存到存储设备。

  6. 资源管理:确保在处理完成后释放图像数据占用的资源,避免内存泄漏。

注意,处理图像数据时,应考虑性能优化和内存管理,确保应用流畅运行。

如果问题依旧没法解决请联系官网客服,官网地址是:https://www.itying.com/category-93-b0.html。

回到顶部