HarmonyOS 鸿蒙Next 相机native方案中OH_ImageReceiver接收第二路预览流,如何对ImageReceiver接收到数据进行处理
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
但是又出了新的问题:旋转后数据经过编码器编码后数据还是不对。
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><uint8_t *>(nv21Ptr);
uint8_t *uv_src_plane = <span class="hljs-keyword">const_cast</span><uint8_t *>(nv21Ptr) + y_size;
<span class="hljs-built_in">std</span>::unique_ptr<uint8_t[]> src2Buffer = <span class="hljs-built_in">std</span>::make_unique<uint8_t[]>(dstBufferSize);
uint8_t *src2Ptr = src2Buffer.get();
uint8_t *y_src2_plane = <span class="hljs-keyword">const_cast</span><uint8_t *>(src2Ptr);
uint8_t *uv_src2_plane = <span class="hljs-keyword">const_cast</span><uint8_t *>(src2Ptr) + y_size;
<span class="hljs-built_in">std</span>::unique_ptr<uint8_t[]> dstBuffer = <span class="hljs-built_in">std</span>::make_unique<uint8_t[]>(dstBufferSize);
uint8_t *dstPtr = dstBuffer.get();
uint8_t *y_dist_plane = <span class="hljs-keyword">const_cast</span><uint8_t *>(dstPtr);
uint8_t *uv_dist_plane = <span class="hljs-keyword">const_cast</span><uint8_t *>(dstPtr) + y_size;
uint8_t *u_dist_plane = <span class="hljs-keyword">const_cast</span><uint8_t *>(dstPtr) + y_size;
uint8_t *v_dist_plane = <span class="hljs-keyword">const_cast</span><uint8_t *>(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()->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><<span class="hljs-keyword">const</span> <span class="hljs-keyword">char</span>*>(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->mTargetFilters) {
filter->CallbackData(VCBufferTypeBytes, &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) => {
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) => {
<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(() => {
<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(() => {
<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 () => {
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 () => {
<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) => {
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><camera.CameraDevice> = [];
<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) => {
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) => {
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><camera.SceneMode> = [];
<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) => {
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) => {
<span class="hljs-keyword">if</span> (profile.size.width == <span class="hljs-keyword">this</span>.imageSize.width && 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) => {
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) => {
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) => {
<span class="hljs-keyword">if</span> (profile.size.width == <span class="hljs-keyword">this</span>.imageSize.width && 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<camera.PhotoSession>(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) => {
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<uint8_t[]> dstBuffer = std::make_unique<uint8_t[]>(dstBufferSize);
uint8_t *dstPtr = dstBuffer.get();
memcpy(dstPtr, srcBuffer, dstBufferSize);
OH_ImageSourceNative *imageSource;
ret = OH_ImageSourceNative_CreateFromData(dstPtr, dstBufferSize, &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(&decodingOptions);
ret = OH_DecodingOptions_SetPixelFormat(decodingOptions, PIXEL_FORMAT_NV21);
ret = OH_ImageSourceNative_CreatePixelmap(imageSource, decodingOptions, &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
接收的第二路预览流数据,通常涉及以下几个步骤:
-
初始化ImageReceiver:确保
OH_ImageReceiver
已正确初始化,并配置为接收第二路预览流。 -
数据回调处理:在
OH_ImageReceiver
的回调函数中处理接收到的图像数据。这通常包括从回调参数中提取图像数据。 -
数据格式转换:根据需求,将接收到的图像数据转换为适合后续处理的格式。例如,从YUV转换为RGB或进行分辨率调整。
-
图像处理:对转换后的图像数据进行处理,如滤镜应用、图像增强、目标检测等。
-
显示或存储:将处理后的图像数据发送到显示设备或保存到存储设备。
-
资源管理:确保在处理完成后释放图像数据占用的资源,避免内存泄漏。
注意,处理图像数据时,应考虑性能优化和内存管理,确保应用流畅运行。
如果问题依旧没法解决请联系官网客服,官网地址是:https://www.itying.com/category-93-b0.html。