HarmonyOS鸿蒙Next中OH_NativeWindow_CreateNativeWindowFromSurfaceId返回0
HarmonyOS鸿蒙Next中OH_NativeWindow_CreateNativeWindowFromSurfaceId返回0
import { camera } from '@kit.CameraKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { abilityAccessCtrl, Permissions } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import streamingConnection from 'libstreaming.so';
import playerConnection from 'libplayer.so';
// import { camera } from '@kit.CameraKit'
// import { camera } from '@kit.CameraKit';
// import { BusinessError } from '@kit.BasicServicesKit';
import { common } from '@kit.AbilityKit';
import { buffer } from '@kit.ArkTS';
// import { CameraDataModel } from '../model/CameraDateModel';
const DOMAIN = 0x0000;
const TAG = 'SecondPage'
@Entry
@Component
struct Index {
private streamComponentCtl: XComponentController = new XComponentController();
private playComponentCtl: XComponentController = new XComponentController();
private streamComponentSurfaceId: string = '';
private playComponentSurfaceId: string = '';
@State imageWidth: number = 1920;
@State imageHeight: number = 1080;
private cameraManager: camera.CameraManager | undefined = undefined;
private cameras: Array<camera.CameraDevice> | Array<camera.CameraDevice> = [];
private cameraInput: camera.CameraInput | undefined = undefined;
private previewOutput: camera.PreviewOutput | undefined = undefined;
private session: camera.VideoSession | undefined = undefined;
private uiContext: UIContext = this.getUIContext();
private context: Context | undefined = this.uiContext.getHostContext();
private cameraPermission: Permissions = 'ohos.permission.CAMERA'; // 申请权限相关问题可参考本篇开头的申请相关权限文档
@State isShow: boolean = false;
@State showText: string = ''
private encoderVideoOutput:camera.VideoOutput | undefined = undefined;
private peerConnection: number = 0;
// 读取文件,解析为对象
getRawFileJsonData() {
let context = this.getUIContext().getHostContext() as common.UIAbilityContext;
let value: Uint8Array = context.resourceManager.getRawFileContentSync('join.json');
let citystr = buffer.from(value.buffer).toString();
let cityObj: ESObject = JSON.parse(citystr)
console.info('join getRawFileJsonData:', JSON.stringify(cityObj) )
let videoProduceArrayvalue: Uint8Array = context.resourceManager.getRawFileContentSync('videoProduce.json');
let videoProducestr = buffer.from(videoProduceArrayvalue.buffer).toString();
let videoProduceObj: ESObject = JSON.parse(videoProducestr)
console.info('videoProduceObj:', JSON.stringify(videoProduceObj) )
this.peerConnection = streamingConnection.DefineObject();
streamingConnection.getValueStringJson(this.peerConnection, JSON.stringify(cityObj), JSON.stringify(videoProduceObj));
let filesDir = context.filesDir;
// console.info('getRawFileJsonData:', filesDir );
console.info(`initCamera getRawFileJsonData filesDir: ${filesDir} `);
}
async requestPermissionsFn(): Promise<void> {
hilog.info(0x008666, '', "requestPermissionsFn is printf ");
let atManager = abilityAccessCtrl.createAtManager();
if (this.context) {
let res = await atManager.requestPermissionsFromUser(this.context, [this.cameraPermission]);
for (let i = 0; i < res.permissions.length; i++) {
if (this.cameraPermission.toString() === res.permissions[i] && res.authResults[i] === 0) {
this.isShow = true;
}
}
hilog.info(0x008666, '', "requestPermissionsFn is 111 ");
}
hilog.info(0x008666, '', "requestPermissionsFn is 666 ");
}
async requestPermissions() {
let atManager = abilityAccessCtrl.createAtManager();
let res = await atManager
.requestPermissionsFromUser(this.context, [
'ohos.permission.WRITE_IMAGEVIDEO',
'ohos.permission.MEDIA_LOCATION',
'ohos.permission.READ_MEDIA',
'ohos.permission.WRITE_MEDIA',
'ohos.permission.MICROPHONE',
'ohos.permission.GET_NETWORK_INFO',
'ohos.permission.MEDIA_LOCATION',
'ohos.permission.INTERNET',
'ohos.permission.READ_IMAGEVIDEO',
'ohos.permission.KEEP_BACKGROUND_RUNNING',
'ohos.permission.SYSTEM_FLOAT_WINDOW',
'ohos.permission.FILE_ACCESS_PERSIST',
'ohos.permission.READ_WRITE_DESKTOP_DIRECTORY',
'ohos.permission.READ_WRITE_DOWNLOAD_DIRECTORY',
'ohos.permission.READ_WRITE_DOCUMENTS_DIRECTORY'
])
.then((data) => {
this.showText = JSON.stringify(data.permissions)
console.log('data permissions:' + data.permissions)
console.log('data result:' + data.authResults)
})
}
aboutToAppear(): void {
this.requestPermissionsFn();
this.requestPermissions();
this.getRawFileJsonData();
}
onPageShow(): void {
console.info('onPageShow');
if (this.streamComponentSurfaceId !== '') {
this.initCamera();
}
}
onPageHide(): void {
console.info('onPageHide');
this.releaseCamera();
}
build() {
Column() {
if (this.isShow) {
XComponent({
id: 'componentId',
type: XComponentType.SURFACE,
controller: this.streamComponentCtl
})
.onLoad(async () => {
console.info('onLoad is called');
this.streamComponentCtl.setXComponentSurfaceSize({surfaceWidth: 1920,surfaceHeight: 1080});
this.streamComponentCtl.setXComponentSurfaceRect({surfaceWidth: 1920,surfaceHeight: 1080});
this.streamComponentSurfaceId = this.streamComponentCtl.getXComponentSurfaceId(); // 获取组件surfaceId。
// 初始化相机,组件实时渲染每帧预览流数据。
this.initCamera();
})
.width(/*this.uiContext.px2vp(this.imageHeight)*/ '100%')
.height(/*this.uiContext.px2vp(this.imageWidth)*/ '40%')
.id('streamComponentCtl')
// .enableAnalyzer(true)
// .position({ x: 400, y: 10 })
// .backgroundColor('#00000000')
XComponent({
id: 'componentId',
type: XComponentType.SURFACE,
controller: this.playComponentCtl
})
.width('100%')
.height('40%')
.onLoad(async () => {
this.playComponentCtl.setXComponentSurfaceSize({surfaceWidth: 1280,surfaceHeight: 720});
this.playComponentCtl.setXComponentSurfaceRect({surfaceWidth: 1280,surfaceHeight: 720});
this.playComponentSurfaceId = this.playComponentCtl.getXComponentSurfaceId();
console.info('playComponentSurfaceId : ' + this.playComponentSurfaceId);
streamingConnection.createDecoder(this.peerConnection, BigInt (this.playComponentSurfaceId) );
})
.id('playComponentCtl_')
// .enableAnalyzer(true)
// .position({ x: 400, y: 10 })
// .backgroundColor('#00000000')
// .align(Alignment.End)
//.margin({ top: '100%', left: '100%' })
Button('切换屏幕').onClick(() => {
// this.xAh = '100%'
// this.xBw = '50%'
// this.xbM = '20%'
// this.xbM_L = '50%'
// window.getLastWindow(this.getUIContext()?.getHostContext(), (err, win) => {
// win.setPreferredOrientation(window.Orientation.LANDSCAPE_INVERTED)
// })
})
}
}
.justifyContent(FlexAlign.Center)
.height('100%')
.width('100%')
}
getCameraDevices(cameraManager: camera.CameraManager): Array<camera.CameraDevice> {
let cameraArray: Array<camera.CameraDevice> = cameraManager.getSupportedCameras();
if (cameraArray != undefined && cameraArray.length > 0) {
for (let index = 0; index < cameraArray.length; index++) {
console.info('cameraId : ' + cameraArray[index].cameraId); // 获取相机ID。
console.info('cameraPosition : ' + cameraArray[index].cameraPosition); // 获取相机位置。
console.info('cameraType : ' + cameraArray[index].cameraType); // 获取相机类型。
console.info('connectionType : ' + cameraArray[index].connectionType); // 获取相机连接类型。
hilog.info(0x008666, TAG, "getCameraDevices is printf ");
}
return cameraArray;
} else {
console.error("cameraManager.getSupportedCameras error");
return [];
}
}
// 初始化相机。
async initCamera(): Promise<void> {
console.info(`initCamera previewOutput streamComponentSurfaceId:${this.streamComponentSurfaceId} filesDir: ${getContext().filesDir} `);
hilog.info(DOMAIN, 'testTag', 'initCamera NAPI 2 + 3 = %{public}d', 5);
let encoderSurfaceId: string = streamingConnection.startConnection(this.peerConnection);
try {
// 获取相机管理器实例。
this.cameraManager = camera.getCameraManager(this.context);
if (!this.cameraManager) {
console.error('initCamera getCameraManager');
return;
}
// 获取当前设备支持的相机device列表。
this.cameras = this.cameraManager.getSupportedCameras();
if (!this.cameras) {
console.error('initCamera getSupportedCameras');
}
if (this.cameras != undefined && this.cameras.length > 0) {
for (let index = 0; index < this.cameras.length; index++) {
console.info('cameraId : ' + this.cameras[index].cameraId); // 获取相机ID。
console.info('cameraPosition : ' + this.cameras[index].cameraPosition); // 获取相机位置。
console.info('cameraType : ' + this.cameras[index].cameraType); // 获取相机类型。
console.info('connectionType : ' + this.cameras[index].connectionType); // 获取相机连接类型。
hilog.info(0x008666, TAG, "getCameraDevices is printf ");
}
}
// 选择一个相机device,创建cameraInput输出对象。
this.cameraInput = this.cameraManager.createCameraInput(this.cameras[1]);
if (!this.cameraInput) {
console.error('initCamera createCameraInput');
return;
}
// 打开相机。
await this.cameraInput.open();
// 获取相机device支持的profile。
let capability: camera.CameraOutputCapability =
this.cameraManager.getSupportedOutputCapability(this.cameras[0], camera.SceneMode.NORMAL_VIDEO);
if (!capability || capability.previewProfiles.length === 0) {
console.error('capability is null || []');
this.releaseCamera();
return;
}
let minRatioDiff : number = 0.1;
let surfaceRatio : number = this.imageWidth / this.imageHeight; // 最接近16:9宽高比。
let previewProfile: camera.Profile = capability.previewProfiles[0];
console.info(`initCamera width:${previewProfile.size.width} height:${previewProfile.size.height}`);
// 应用开发者根据实际业务需求选择一个支持的预览流previewProfile。
// 此处以选择CAMERA_FORMAT_YUV_420_SP(NV21)格式、满足限定条件分辨率的预览流previewProfile为例。
for (let index = 0; index < capability.previewProfiles.length; index++) {
const tempProfile = capability.previewProfiles[index];
console.info(`get camera format:${tempProfile.format} index:${index}`);
let tempRatio = tempProfile.size.width >= tempProfile.size.height ?
tempProfile.size.width / tempProfile.size.height : tempProfile.size.height / tempProfile.size.width;
let currentRatio = Math.abs(tempRatio - surfaceRatio);
if (currentRatio <= minRatioDiff && tempProfile.format == camera.CameraFormat.CAMERA_FORMAT_YUV_420_SP) {
previewProfile = tempProfile;
break;
}
}
previewProfile.size.width = previewProfile.size.width*3;
previewProfile.size.height = previewProfile.size.height*3;
this.imageWidth = previewProfile.size.width; // 更新xComponent组件的宽。
this.imageHeight = previewProfile.size.height; // 更新xComponent组件的高。
console.info(`initCamera imageWidth:${this.imageWidth} imageHeight:${this.imageHeight}`); // initCamera imageWidth:1920 imageHeight:1080
// 使用xComponentSurfaceId创建预览。
this.previewOutput = this.cameraManager.createPreviewOutput(previewProfile, this.streamComponentSurfaceId);
if (!this.previewOutput) {
console.error('initCamera createPreviewOutput');
this.releaseCamera();
return;
}
// 创建录像模式相机会话。
let session = this.cameraManager.createSession(camera.SceneMode.NORMAL_VIDEO);
if (!session) {
console.error('session is null');
this.releaseCamera();
return;
}
console.info(`initCamera size0:${capability.videoProfiles[0].size.height} size1:${capability.videoProfiles[1].size.height},width0:${capability.videoProfiles[0].size.width} width1:${capability.videoProfiles[1].size.width} `);
let encodeProfile: camera.VideoProfile = capability.videoProfiles[0];
encodeProfile.size.width = this.imageWidth;
encodeProfile.size.height = this.imageHeight;
//Create the encoder output object
this.encoderVideoOutput = this.cameraManager.createVideoOutput(encodeProfile, encoderSurfaceId);
if (this.encoderVideoOutput === undefined) {
console.error(TAG, 'encoderVideoOutput is undefined');
return;
}
console.info(TAG, 'encoderVideoOutput success');
this.session = session as camera.VideoSession;
// 开始配置会话。
this.session.beginConfig();
// 添加相机设备输入。
this.session.addInput(this.cameraInput);
// 添加预览流输出。
this.session.addOutput(this.previewOutput);
this.session.addOutput(this.encoderVideoOutput);
// 提交会话配置。
await this.session.commitConfig();
// 开始启动已配置的输入输出流。
await this.session.start();
this.encoderVideoOutput.start((err: BusinessError) => {
if (err) {
console.error(TAG, `Failed to start the encoder video output. error: ${JSON.stringify(err)}`)
return
}
console.info(TAG, 'Callback invoked to indicate the encoder video output start success.')
});
console.info(`initCamera start `);
streamingConnection.encoderStart();
} catch (error) {
console.error(`initCamera fail: ${JSON.stringify(error)}`);
this.releaseCamera();
}
}
// 释放相机。
async releaseCamera(): Promise<void> {
console.info('releaseCamera');
// 停止当前会话。
await this.session?.stop().catch((e: BusinessError) => {console.error('Failed to stop session: ', e)});
// 释放相机输入流。
await this.cameraInput?.close().catch((e: BusinessError) => {console.error('Failed to close the camera: ', e)});
// 释放预览输出流。
await this.previewOutput?.release().catch((e: BusinessError) => {console.error('Failed to stop the preview stream: ', e)});
// 释放会话。
await this.session?.release().catch((e: BusinessError) => {console.error('Failed to release session: ', e)});
}
}
void OhoHardWareDecoder::start(uint64_t surfaceId)
{
OH_AVErrCode ret;
this->nativeWindow = nullptr;
// 基于步骤1.1中获取的surfaceId创建对应的nativeWindow实例。
int success = OH_NativeWindow_CreateNativeWindowFromSurfaceId(surfaceId, &nativeWindow);
if (!success || nativeWindow == nullptr) {
OH_LOG_ERROR(LOG_APP, "OH_NativeWindow_CreateNativeWindowFromSurfaceId Failed to create native window ,ret %{public}d ",success);
// return; // 处理错误
}
// OH_LOG_ERROR(LOG_APP, "OH_NativeWindow_CreateNativeWindowFromSurfaceId ret %{public}d ", ret1); // 异常处理。
// 设置surface。 配置送显窗口参数。
if(nativeWindow == nullptr)
{
OH_LOG_ERROR(LOG_APP, "nativeWindow error"); // 异常处理。
}
ret = OH_VideoDecoder_SetSurface(videoDec, nativeWindow); // nativeWindow通过以上两种方式获取。
if (ret != AV_ERR_OK) {
OH_LOG_ERROR(LOG_APP,"OH_VideoDecoder_SetSurface error "); // 异常处理。
}
// 配置视频与显示屏匹配模式(缓冲区按原比例缩放,使得缓冲区的较小边与窗口匹配,较长边超出窗口的部分被视为透明)。
// OH_NativeWindow_NativeWindowSetScalingModeV2(nativeWindow, OH_SCALING_MODE_SCALE_CROP_V2);
ret = OH_VideoDecoder_Prepare(videoDec);
if (ret != AV_ERR_OK) {
OH_LOG_ERROR(LOG_APP,"OH_VideoDecoder_Prepare error "); // 异常处理。
}
// 启动解码更多关于HarmonyOS鸿蒙Next中OH_NativeWindow_CreateNativeWindowFromSurfaceId返回0的实战教程也可以访问 https://www.itying.com/category-93-b0.html
在HarmonyOS Next中,OH_NativeWindow_CreateNativeWindowFromSurfaceId返回0表示函数执行失败。该函数用于通过Surface ID创建NativeWindow对象,返回0通常意味着传入的Surface ID无效或系统资源不足。可能原因包括:Surface ID不存在、已被销毁,或内存分配失败。需检查Surface ID来源及生命周期管理,确保在有效状态下调用。该接口属于Native Window模块,用于图形渲染操作。
更多关于HarmonyOS鸿蒙Next中OH_NativeWindow_CreateNativeWindowFromSurfaceId返回0的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
在HarmonyOS Next中,OH_NativeWindow_CreateNativeWindowFromSurfaceId返回0表示创建NativeWindow失败。从代码分析,问题可能出现在以下几个方面:
-
SurfaceId传递问题:在ArkTS代码中,
playComponentSurfaceId通过getXComponentSurfaceId()获取后,在C++层被转换为BigInt类型传递。需要确认:- SurfaceId是否在XComponent完全加载后获取
- 传递过程中数据类型转换是否正确
- SurfaceId是否有效且不为空
-
时序问题:确保在调用
OH_NativeWindow_CreateNativeWindowFromSurfaceId时:- XComponent已成功创建并初始化
- Surface已就绪可用
- 没有在组件未加载完成时提前获取SurfaceId
-
权限和配置:
- 确认所有必要的媒体权限已正确申请
- 检查XComponent的尺寸设置是否合理
- 验证视频解码器的配置参数
建议在调用OH_NativeWindow_CreateNativeWindowFromSurfaceId前添加日志,确认SurfaceId的值和状态,并检查NativeWindow创建失败的具体错误码。

