1e41f4b71Sopenharmony_ci# Rendering and Drawing Video and Button Components at the Same Layer 2e41f4b71Sopenharmony_ci 3e41f4b71Sopenharmony_ciWith the same-layer rendering feature of ArkWeb, you can render and draw native components at the same layer as the **\<Web>** component for your application. For details about the components that support same-layer rendering, see [NodeRenderType](../reference/apis-arkui/js-apis-arkui-builderNode.md#noderendertype). 4e41f4b71Sopenharmony_ci 5e41f4b71Sopenharmony_ci- To start with, add the Internet permission to the **module.json5** file. For details, see [Declaring Permissions in the Configuration File](../security/AccessToken/declare-permissions.md). 6e41f4b71Sopenharmony_ci 7e41f4b71Sopenharmony_ci ``` 8e41f4b71Sopenharmony_ci "requestPermissions":[ 9e41f4b71Sopenharmony_ci { 10e41f4b71Sopenharmony_ci "name" : "ohos.permission.INTERNET" 11e41f4b71Sopenharmony_ci } 12e41f4b71Sopenharmony_ci ] 13e41f4b71Sopenharmony_ci ``` 14e41f4b71Sopenharmony_ci 15e41f4b71Sopenharmony_ci## Constraints 16e41f4b71Sopenharmony_ciThe following constraints apply when same-layer rendering is used: 17e41f4b71Sopenharmony_ci 18e41f4b71Sopenharmony_ci- W3C standards-based tags cannot be defined as tags for same-layer rendering. 19e41f4b71Sopenharmony_ci 20e41f4b71Sopenharmony_ci- The **<object>** tags and **\<embed>** tags cannot be configured for same-layer rendering at the same time. 21e41f4b71Sopenharmony_ci 22e41f4b71Sopenharmony_ci- To deliver best possible performance, keep the number of tags at the same layer on a page within five. 23e41f4b71Sopenharmony_ci 24e41f4b71Sopenharmony_ci- The maximum height of tags at the same layer is 8192 px, and the maximum texture size is 8192 px. 25e41f4b71Sopenharmony_ci 26e41f4b71Sopenharmony_ci- Only one nesting level is supported for the **\<Web>** component. If multiple nesting levels are detected, an error message is displayed. 27e41f4b71Sopenharmony_ci 28e41f4b71Sopenharmony_ci- The touchscreen events supported in the region for same-layer rendering include swiping, taping, scaling, and long pressing, while dragging is not supported. 29e41f4b71Sopenharmony_ci 30e41f4b71Sopenharmony_ci- When same-layer rendering is enabled, web pages opened by the **\<Web>** component do not support the pinch gesture or scale APIs, including [initialScale](../reference/apis-arkweb/ts-basic-components-web.md#initialscale), [zoom](../reference/apis-arkweb/js-apis-webview.md#zoom), [zoomIn](../reference/apis-arkweb/js-apis-webview.md#zoomin), and [zoomOut](../reference/apis-arkweb/js-apis-webview.md#zoomout). 31e41f4b71Sopenharmony_ci 32e41f4b71Sopenharmony_ci- The region for same-layer rendering does not support mouse, keyboard, and touchpad events. 33e41f4b71Sopenharmony_ci 34e41f4b71Sopenharmony_ci- When same-layer rendering is enabled, web pages opened by the **\<Web>** component do not support the unified rendering mode [RenderMode](../reference/apis-arkweb/ts-basic-components-web.md#rendermode). 35e41f4b71Sopenharmony_ci 36e41f4b71Sopenharmony_ci 37e41f4b71Sopenharmony_ci## Drawing the XComponent+AVPlayer and Button Components 38e41f4b71Sopenharmony_ci 39e41f4b71Sopenharmony_ci### Enabling Same-Layer Rendering 40e41f4b71Sopenharmony_ci 41e41f4b71Sopenharmony_ciYou can enable or disable same-layer rendering through [enableNativeEmbedMode()](../reference/apis-arkweb/ts-basic-components-web.md#enablenativeembedmode11). To use same-layer rendering, the **\<embed>** element must be explicitly used in the HTML file, and the **type** attribute of the element must start with **native/**. The background of the elements corresponding to the tags at the same layer is transparent. 42e41f4b71Sopenharmony_ci 43e41f4b71Sopenharmony_ci- Example of using same-layer rendering on the application side: 44e41f4b71Sopenharmony_ci 45e41f4b71Sopenharmony_ci ```ts 46e41f4b71Sopenharmony_ci // HAP's src/main/ets/pages/Index.ets 47e41f4b71Sopenharmony_ci // Create a NodeController instance. 48e41f4b71Sopenharmony_ci import { webview } from '@kit.ArkWeb'; 49e41f4b71Sopenharmony_ci import { UIContext, NodeController, BuilderNode, NodeRenderType, FrameNode } from "@kit.ArkUI"; 50e41f4b71Sopenharmony_ci import { AVPlayerDemo } from './PlayerDemo'; 51e41f4b71Sopenharmony_ci 52e41f4b71Sopenharmony_ci @Observed 53e41f4b71Sopenharmony_ci declare class Params { 54e41f4b71Sopenharmony_ci textOne : string 55e41f4b71Sopenharmony_ci textTwo : string 56e41f4b71Sopenharmony_ci width : number 57e41f4b71Sopenharmony_ci height : number 58e41f4b71Sopenharmony_ci } 59e41f4b71Sopenharmony_ci 60e41f4b71Sopenharmony_ci declare class nodeControllerParams { 61e41f4b71Sopenharmony_ci surfaceId : string 62e41f4b71Sopenharmony_ci type : string 63e41f4b71Sopenharmony_ci renderType : NodeRenderType 64e41f4b71Sopenharmony_ci embedId : string 65e41f4b71Sopenharmony_ci width : number 66e41f4b71Sopenharmony_ci height : number 67e41f4b71Sopenharmony_ci } 68e41f4b71Sopenharmony_ci 69e41f4b71Sopenharmony_ci // The NodeController instance must be used with a NodeContainer for controlling and feeding back the behavior of the nodes in the container. 70e41f4b71Sopenharmony_ci class MyNodeController extends NodeController { 71e41f4b71Sopenharmony_ci private rootNode: BuilderNode<[Params]> | undefined | null; 72e41f4b71Sopenharmony_ci private embedId_ : string = ""; 73e41f4b71Sopenharmony_ci private surfaceId_ : string = ""; 74e41f4b71Sopenharmony_ci private renderType_ :NodeRenderType = NodeRenderType.RENDER_TYPE_DISPLAY; 75e41f4b71Sopenharmony_ci private width_ : number = 0; 76e41f4b71Sopenharmony_ci private height_ : number = 0; 77e41f4b71Sopenharmony_ci private type_ : string = ""; 78e41f4b71Sopenharmony_ci private isDestroy_ : boolean = false; 79e41f4b71Sopenharmony_ci 80e41f4b71Sopenharmony_ci setRenderOption(params : nodeControllerParams) { 81e41f4b71Sopenharmony_ci this.surfaceId_ = params.surfaceId; 82e41f4b71Sopenharmony_ci this.renderType_ = params.renderType; 83e41f4b71Sopenharmony_ci this.embedId_ = params.embedId; 84e41f4b71Sopenharmony_ci this.width_ = params.width; 85e41f4b71Sopenharmony_ci this.height_ = params.height; 86e41f4b71Sopenharmony_ci this.type_ = params.type; 87e41f4b71Sopenharmony_ci } 88e41f4b71Sopenharmony_ci // Method that must be overridden. It is used to build the number of nodes and return the number of nodes that will be mounted to the corresponding NodeContainer. 89e41f4b71Sopenharmony_ci // Called when the corresponding NodeContainer is created or called by the rebuild method. 90e41f4b71Sopenharmony_ci makeNode(uiContext: UIContext): FrameNode | null{ 91e41f4b71Sopenharmony_ci if (this.isDestroy_) { // rootNode is null. 92e41f4b71Sopenharmony_ci return null; 93e41f4b71Sopenharmony_ci } 94e41f4b71Sopenharmony_ci if (!this.rootNode) { // When rootNode is set to undefined 95e41f4b71Sopenharmony_ci this.rootNode = new BuilderNode(uiContext, { surfaceId: this.surfaceId_, type: this.renderType_}); 96e41f4b71Sopenharmony_ci if (this.type_ === 'native/video') { 97e41f4b71Sopenharmony_ci this.rootNode.build(wrapBuilder(VideoBuilder), {textOne: "myButton", width : this.width_, height : this.height_}); 98e41f4b71Sopenharmony_ci } else { 99e41f4b71Sopenharmony_ci // other 100e41f4b71Sopenharmony_ci } 101e41f4b71Sopenharmony_ci } 102e41f4b71Sopenharmony_ci // Return the FrameNode object. 103e41f4b71Sopenharmony_ci return this.rootNode.getFrameNode(); 104e41f4b71Sopenharmony_ci } 105e41f4b71Sopenharmony_ci 106e41f4b71Sopenharmony_ci setBuilderNode(rootNode: BuilderNode<Params[]> | null): void{ 107e41f4b71Sopenharmony_ci this.rootNode = rootNode; 108e41f4b71Sopenharmony_ci } 109e41f4b71Sopenharmony_ci 110e41f4b71Sopenharmony_ci getBuilderNode(): BuilderNode<[Params]> | undefined | null{ 111e41f4b71Sopenharmony_ci return this.rootNode; 112e41f4b71Sopenharmony_ci } 113e41f4b71Sopenharmony_ci 114e41f4b71Sopenharmony_ci updateNode(arg: Object): void { 115e41f4b71Sopenharmony_ci this.rootNode?.update(arg); 116e41f4b71Sopenharmony_ci } 117e41f4b71Sopenharmony_ci getEmbedId() : string { 118e41f4b71Sopenharmony_ci return this.embedId_; 119e41f4b71Sopenharmony_ci } 120e41f4b71Sopenharmony_ci 121e41f4b71Sopenharmony_ci setDestroy(isDestroy : boolean) : void { 122e41f4b71Sopenharmony_ci this.isDestroy_ = isDestroy; 123e41f4b71Sopenharmony_ci if (this.isDestroy_) { 124e41f4b71Sopenharmony_ci this.rootNode = null; 125e41f4b71Sopenharmony_ci } 126e41f4b71Sopenharmony_ci } 127e41f4b71Sopenharmony_ci 128e41f4b71Sopenharmony_ci postEvent(event: TouchEvent | undefined) : boolean { 129e41f4b71Sopenharmony_ci return this.rootNode?.postTouchEvent(event) as boolean 130e41f4b71Sopenharmony_ci } 131e41f4b71Sopenharmony_ci } 132e41f4b71Sopenharmony_ci 133e41f4b71Sopenharmony_ci @Component 134e41f4b71Sopenharmony_ci struct VideoComponent { 135e41f4b71Sopenharmony_ci @ObjectLink params: Params 136e41f4b71Sopenharmony_ci @State bkColor: Color = Color.Red 137e41f4b71Sopenharmony_ci mXComponentController: XComponentController = new XComponentController(); 138e41f4b71Sopenharmony_ci @State player_changed: boolean = false; 139e41f4b71Sopenharmony_ci player?: AVPlayerDemo; 140e41f4b71Sopenharmony_ci 141e41f4b71Sopenharmony_ci build() { 142e41f4b71Sopenharmony_ci Column() { 143e41f4b71Sopenharmony_ci Button(this.params.textOne) 144e41f4b71Sopenharmony_ci 145e41f4b71Sopenharmony_ci XComponent({ id: 'video_player_id', type: XComponentType.SURFACE, controller: this.mXComponentController}) 146e41f4b71Sopenharmony_ci .border({width: 1, color: Color.Red}) 147e41f4b71Sopenharmony_ci .onLoad(() => { 148e41f4b71Sopenharmony_ci this.player = new AVPlayerDemo(); 149e41f4b71Sopenharmony_ci this.player.setSurfaceID(this.mXComponentController.getXComponentSurfaceId()); 150e41f4b71Sopenharmony_ci this.player_changed = !this.player_changed; 151e41f4b71Sopenharmony_ci this.player.avPlayerLiveDemo() 152e41f4b71Sopenharmony_ci }) 153e41f4b71Sopenharmony_ci .width(300) 154e41f4b71Sopenharmony_ci .height(200) 155e41f4b71Sopenharmony_ci } 156e41f4b71Sopenharmony_ci // The width and height of the outermost container in the custom component must be the width and height of the tag at the same layer. 157e41f4b71Sopenharmony_ci .width(this.params.width) 158e41f4b71Sopenharmony_ci .height(this.params.height) 159e41f4b71Sopenharmony_ci } 160e41f4b71Sopenharmony_ci } 161e41f4b71Sopenharmony_ci // In @Builder, add the specific dynamic component content. 162e41f4b71Sopenharmony_ci @Builder 163e41f4b71Sopenharmony_ci function VideoBuilder(params: Params) { 164e41f4b71Sopenharmony_ci VideoComponent({ params: params }) 165e41f4b71Sopenharmony_ci .backgroundColor(Color.Gray) 166e41f4b71Sopenharmony_ci } 167e41f4b71Sopenharmony_ci 168e41f4b71Sopenharmony_ci @Entry 169e41f4b71Sopenharmony_ci @Component 170e41f4b71Sopenharmony_ci struct WebIndex { 171e41f4b71Sopenharmony_ci browserTabController: WebviewController = new webview.WebviewController() 172e41f4b71Sopenharmony_ci private nodeControllerMap: Map<string, MyNodeController> = new Map(); 173e41f4b71Sopenharmony_ci @State componentIdArr: Array<string> = []; 174e41f4b71Sopenharmony_ci 175e41f4b71Sopenharmony_ci aboutToAppear() { 176e41f4b71Sopenharmony_ci // Enable web frontend page debugging. 177e41f4b71Sopenharmony_ci webview.WebviewController.setWebDebuggingAccess(true); 178e41f4b71Sopenharmony_ci } 179e41f4b71Sopenharmony_ci 180e41f4b71Sopenharmony_ci build(){ 181e41f4b71Sopenharmony_ci Row() { 182e41f4b71Sopenharmony_ci Column() { 183e41f4b71Sopenharmony_ci Stack() { 184e41f4b71Sopenharmony_ci ForEach(this.componentIdArr, (componentId: string) => { 185e41f4b71Sopenharmony_ci NodeContainer(this.nodeControllerMap.get(componentId)) 186e41f4b71Sopenharmony_ci }, (embedId: string) => embedId) 187e41f4b71Sopenharmony_ci // Load the local test.html page. 188e41f4b71Sopenharmony_ci Web({ src: $rawfile("test.html"), controller: this.browserTabController }) 189e41f4b71Sopenharmony_ci // Enable same-layer rendering. 190e41f4b71Sopenharmony_ci .enableNativeEmbedMode(true) 191e41f4b71Sopenharmony_ci // Obtain the lifecycle change data of the embed element. 192e41f4b71Sopenharmony_ci .onNativeEmbedLifecycleChange((embed) => { 193e41f4b71Sopenharmony_ci console.log("NativeEmbed surfaceId" + embed.surfaceId); 194e41f4b71Sopenharmony_ci // 1. If embed.info.id is used as the key for mapping nodeController, explicitly specify the ID on the HTML5 page. 195e41f4b71Sopenharmony_ci const componentId = embed.info?.id?.toString() as string 196e41f4b71Sopenharmony_ci if (embed.status == NativeEmbedStatus.CREATE) { 197e41f4b71Sopenharmony_ci console.log("NativeEmbed create" + JSON.stringify(embed.info)) 198e41f4b71Sopenharmony_ci // Create a NodeController instance, set parameters, and rebuild. 199e41f4b71Sopenharmony_ci let nodeController = new MyNodeController() 200e41f4b71Sopenharmony_ci // 1. The unit of embed.info.width and embed.info.height is px, which needs to be converted to the default unit vp on the ets side. 201e41f4b71Sopenharmony_ci nodeController.setRenderOption({surfaceId : embed.surfaceId as string, type : embed.info?.type as string, 202e41f4b71Sopenharmony_ci renderType : NodeRenderType.RENDER_TYPE_TEXTURE, embedId : embed.embedId as string, 203e41f4b71Sopenharmony_ci width : px2vp(embed.info?.width), height : px2vp(embed.info?.height)}) 204e41f4b71Sopenharmony_ci nodeController.setDestroy(false); 205e41f4b71Sopenharmony_ci // Save the NodeController instance to the map, with the ID attribute of the embed element passed in by the Web component as the key. 206e41f4b71Sopenharmony_ci this.nodeControllerMap.set(componentId, nodeController) 207e41f4b71Sopenharmony_ci // Save the ID attribute of the embed element passed in by the Web component to the @State decorated array variable for creating a node container dynamically. The push action must be executed after the set action. 208e41f4b71Sopenharmony_ci this.componentIdArr.push(componentId) 209e41f4b71Sopenharmony_ci } else if (embed.status == NativeEmbedStatus.UPDATE) { 210e41f4b71Sopenharmony_ci let nodeController = this.nodeControllerMap.get(componentId) 211e41f4b71Sopenharmony_ci nodeController?.updateNode({textOne: 'update', width: px2vp(embed.info?.width), height: px2vp(embed.info?.height)} as ESObject) 212e41f4b71Sopenharmony_ci } else { 213e41f4b71Sopenharmony_ci let nodeController = this.nodeControllerMap.get(componentId); 214e41f4b71Sopenharmony_ci nodeController?.setDestroy(true) 215e41f4b71Sopenharmony_ci this.nodeControllerMap.clear(); 216e41f4b71Sopenharmony_ci this.componentIdArr.length = 0; 217e41f4b71Sopenharmony_ci } 218e41f4b71Sopenharmony_ci })// Obtain the touch event information of components for same-layer rendering. 219e41f4b71Sopenharmony_ci .onNativeEmbedGestureEvent((touch) => { 220e41f4b71Sopenharmony_ci console.log("NativeEmbed onNativeEmbedGestureEvent" + JSON.stringify(touch.touchEvent)); 221e41f4b71Sopenharmony_ci this.componentIdArr.forEach((componentId: string) => { 222e41f4b71Sopenharmony_ci let nodeController = this.nodeControllerMap.get(componentId) 223e41f4b71Sopenharmony_ci // Send the obtained event of the region at the same layer to the nodeController corresponding to embedId of the region. 224e41f4b71Sopenharmony_ci if (nodeController?.getEmbedId() === touch.embedId) { 225e41f4b71Sopenharmony_ci let ret = nodeController?.postEvent(touch.touchEvent) 226e41f4b71Sopenharmony_ci if (ret) { 227e41f4b71Sopenharmony_ci console.log("onNativeEmbedGestureEvent success " + componentId) 228e41f4b71Sopenharmony_ci } else { 229e41f4b71Sopenharmony_ci console.log("onNativeEmbedGestureEvent fail " + componentId) 230e41f4b71Sopenharmony_ci } 231e41f4b71Sopenharmony_ci if (touch.result) { 232e41f4b71Sopenharmony_ci // Notify the <Web> component of the gesture event consumption result. 233e41f4b71Sopenharmony_ci touch.result.setGestureEventResult(ret); 234e41f4b71Sopenharmony_ci } 235e41f4b71Sopenharmony_ci } 236e41f4b71Sopenharmony_ci }) 237e41f4b71Sopenharmony_ci }) 238e41f4b71Sopenharmony_ci } 239e41f4b71Sopenharmony_ci } 240e41f4b71Sopenharmony_ci } 241e41f4b71Sopenharmony_ci } 242e41f4b71Sopenharmony_ci } 243e41f4b71Sopenharmony_ci ``` 244e41f4b71Sopenharmony_ci 245e41f4b71Sopenharmony_ci- Example of using video playback on the application side: 246e41f4b71Sopenharmony_ci 247e41f4b71Sopenharmony_ci ```ts 248e41f4b71Sopenharmony_ci // HAP's src/main/ets/pages/PlayerDemo.ets 249e41f4b71Sopenharmony_ci import { media } from '@kit.MediaKit'; 250e41f4b71Sopenharmony_ci import { BusinessError } from '@ohos.base'; 251e41f4b71Sopenharmony_ci 252e41f4b71Sopenharmony_ci export class AVPlayerDemo { 253e41f4b71Sopenharmony_ci private count: number = 0; 254e41f4b71Sopenharmony_ci private surfaceID: string = ''; // The surfaceID parameter specifies the window used to display the video. Its value is obtained through XComponent. 255e41f4b71Sopenharmony_ci private isSeek: boolean = true; // Specify whether the seek operation is supported. 256e41f4b71Sopenharmony_ci 257e41f4b71Sopenharmony_ci setSurfaceID(surface_id: string){ 258e41f4b71Sopenharmony_ci console.log('setSurfaceID : ' + surface_id); 259e41f4b71Sopenharmony_ci this.surfaceID = surface_id; 260e41f4b71Sopenharmony_ci } 261e41f4b71Sopenharmony_ci // Set AVPlayer callback functions. 262e41f4b71Sopenharmony_ci setAVPlayerCallback(avPlayer: media.AVPlayer) { 263e41f4b71Sopenharmony_ci // Callback function for the seek operation. 264e41f4b71Sopenharmony_ci avPlayer.on('seekDone', (seekDoneTime: number) => { 265e41f4b71Sopenharmony_ci console.info(`AVPlayer seek succeeded, seek time is ${seekDoneTime}`); 266e41f4b71Sopenharmony_ci }) 267e41f4b71Sopenharmony_ci // Callback function for errors. If an error occurs during the operation on the AVPlayer, reset() is called to reset the AVPlayer. 268e41f4b71Sopenharmony_ci avPlayer.on('error', (err: BusinessError) => { 269e41f4b71Sopenharmony_ci console.error(`Invoke avPlayer failed, code is ${err.code}, message is ${err.message}`); 270e41f4b71Sopenharmony_ci avPlayer.reset(); 271e41f4b71Sopenharmony_ci }) 272e41f4b71Sopenharmony_ci // Callback function for state changes. 273e41f4b71Sopenharmony_ci avPlayer.on('stateChange', async (state: string, reason: media.StateChangeReason) => { 274e41f4b71Sopenharmony_ci switch (state) { 275e41f4b71Sopenharmony_ci case 'idle': // This state is reported upon a successful callback of reset(). 276e41f4b71Sopenharmony_ci console.info('AVPlayer state idle called.'); 277e41f4b71Sopenharmony_ci avPlayer.release(); // Call release() to release the instance. 278e41f4b71Sopenharmony_ci break; 279e41f4b71Sopenharmony_ci case 'initialized': // This state is reported when the AVPlayer sets the playback source. 280e41f4b71Sopenharmony_ci console.info('AVPlayer state initialized called.'); 281e41f4b71Sopenharmony_ci avPlayer.surfaceId = this.surfaceID; // Set the window to display the video. This setting is not required when a pure audio asset is to be played. 282e41f4b71Sopenharmony_ci avPlayer.prepare(); 283e41f4b71Sopenharmony_ci break; 284e41f4b71Sopenharmony_ci case 'prepared': // This state is reported upon a successful callback of prepare(). 285e41f4b71Sopenharmony_ci console.info('AVPlayer state prepared called.'); 286e41f4b71Sopenharmony_ci avPlayer.play(); // Call play() to start playback. 287e41f4b71Sopenharmony_ci break; 288e41f4b71Sopenharmony_ci case 'playing': // This state is reported upon a successful callback of play(). 289e41f4b71Sopenharmony_ci console.info('AVPlayer state prepared called.'); 290e41f4b71Sopenharmony_ci if(this.count !== 0) { 291e41f4b71Sopenharmony_ci if (this.isSeek) { 292e41f4b71Sopenharmony_ci console.info('AVPlayer start to seek.'); 293e41f4b71Sopenharmony_ci avPlayer.seek(avPlayer.duration); // Call seek() to seek to the end of the video clip. 294e41f4b71Sopenharmony_ci } else { 295e41f4b71Sopenharmony_ci // When the seek operation is not supported, the playback continues until it reaches the end. 296e41f4b71Sopenharmony_ci console.info('AVPlayer wait to play end.'); 297e41f4b71Sopenharmony_ci } 298e41f4b71Sopenharmony_ci } else { 299e41f4b71Sopenharmony_ci avPlayer.pause(); // Call pause() to pause the playback. 300e41f4b71Sopenharmony_ci } 301e41f4b71Sopenharmony_ci this.count++; 302e41f4b71Sopenharmony_ci break; 303e41f4b71Sopenharmony_ci case 'paused': // This state is reported upon a successful callback of pause(). 304e41f4b71Sopenharmony_ci console.info('AVPlayer state paused called.'); 305e41f4b71Sopenharmony_ci avPlayer.play(); // Call play() again to start playback. 306e41f4b71Sopenharmony_ci break; 307e41f4b71Sopenharmony_ci case 'completed': // This state is reported upon the completion of the playback. 308e41f4b71Sopenharmony_ci console.info('AVPlayer state paused called.'); 309e41f4b71Sopenharmony_ci avPlayer.stop(); // Call stop() to stop the playback. 310e41f4b71Sopenharmony_ci break; 311e41f4b71Sopenharmony_ci case 'stopped': // This state is reported upon a successful callback of stop(). 312e41f4b71Sopenharmony_ci console.info('AVPlayer state stopped called.'); 313e41f4b71Sopenharmony_ci avPlayer.reset(); // Call reset() to reset the AVPlayer. 314e41f4b71Sopenharmony_ci break; 315e41f4b71Sopenharmony_ci case 'released': // This state is reported upon the release of the AVPlayer. 316e41f4b71Sopenharmony_ci console.info('AVPlayer state released called.'); 317e41f4b71Sopenharmony_ci break; 318e41f4b71Sopenharmony_ci default: 319e41f4b71Sopenharmony_ci break; 320e41f4b71Sopenharmony_ci } 321e41f4b71Sopenharmony_ci }) 322e41f4b71Sopenharmony_ci } 323e41f4b71Sopenharmony_ci 324e41f4b71Sopenharmony_ci // Set the live stream source through the URL. 325e41f4b71Sopenharmony_ci async avPlayerLiveDemo(){ 326e41f4b71Sopenharmony_ci // Create an AVPlayer instance. 327e41f4b71Sopenharmony_ci let avPlayer: media.AVPlayer = await media.createAVPlayer(); 328e41f4b71Sopenharmony_ci // Set a callback function for state changes. 329e41f4b71Sopenharmony_ci this.setAVPlayerCallback(avPlayer); 330e41f4b71Sopenharmony_ci this.isSeek = false; // The seek operation is not supported. 331e41f4b71Sopenharmony_ci // Replace the URL with the actual URL of the video source. 332e41f4b71Sopenharmony_ci avPlayer.url = 'https://xxx.xxx/demo.mp4'; 333e41f4b71Sopenharmony_ci } 334e41f4b71Sopenharmony_ci } 335e41f4b71Sopenharmony_ci ``` 336e41f4b71Sopenharmony_ci 337e41f4b71Sopenharmony_ci- Example of the frontend page: 338e41f4b71Sopenharmony_ci 339e41f4b71Sopenharmony_ci ```html 340e41f4b71Sopenharmony_ci <!--HAP's src/main/resources/rawfile/test.html--> 341e41f4b71Sopenharmony_ci <!DOCTYPE html> 342e41f4b71Sopenharmony_ci <html> 343e41f4b71Sopenharmony_ci <head> 344e41f4b71Sopenharmony_ci <title>Same-layer rendering test html</title> 345e41f4b71Sopenharmony_ci <meta name="viewport"> 346e41f4b71Sopenharmony_ci </head> 347e41f4b71Sopenharmony_ci <body> 348e41f4b71Sopenharmony_ci <div> 349e41f4b71Sopenharmony_ci <div id="bodyId"> 350e41f4b71Sopenharmony_ci <embed id="nativeVideo" type = "native/video" width="1000" height="1500" src="test" style = "background-color:red"/> 351e41f4b71Sopenharmony_ci </div> 352e41f4b71Sopenharmony_ci </div> 353e41f4b71Sopenharmony_ci </body> 354e41f4b71Sopenharmony_ci </html> 355e41f4b71Sopenharmony_ci ``` 356e41f4b71Sopenharmony_ci 357e41f4b71Sopenharmony_ci  358e41f4b71Sopenharmony_ci 359e41f4b71Sopenharmony_ci### Enabling Same-Layer Rendering and Specifying the Label Name and Custom Type 360e41f4b71Sopenharmony_ci 361e41f4b71Sopenharmony_ciYou can also use [registerNativeEmbedRule(tag: string, type: string)](../reference/apis-arkweb/ts-basic-components-web.md#registernativeembedrule12) to specify the tag and type. 362e41f4b71Sopenharmony_ci 363e41f4b71Sopenharmony_ciFor the **tag** parameter, only **embed** and **object** are supported. For the **type** parameter, you can specify any string. These two parameters are case insensitive: The ArkWeb kernel converts the values into lowercase letters. The **tag** parameter uses the full string for matching, and **type** uses the prefix for matching. 364e41f4b71Sopenharmony_ci 365e41f4b71Sopenharmony_ciIf you do not use this API or the API receives an invalid string (for example, an empty string), the kernel uses the default prefix mode "embed" + "native/". If the specified type is the same as any object or embedded type defined by W3C, as in **registerNativeEmbedRule("object", "application/pdf")**, 366e41f4b71Sopenharmony_ciArkWeb will follow the W3C standard behavior and will not identify it as a tag at the same layer. 367e41f4b71Sopenharmony_ci 368e41f4b71Sopenharmony_ci- Example of using **registerNativeEmbedRule** on the application side: 369e41f4b71Sopenharmony_ci 370e41f4b71Sopenharmony_ci ```ts 371e41f4b71Sopenharmony_ci class MyNodeController extends NodeController { 372e41f4b71Sopenharmony_ci ... 373e41f4b71Sopenharmony_ci makeNode(uiContext: UIContext): FrameNode | null{ 374e41f4b71Sopenharmony_ci 375e41f4b71Sopenharmony_ci if (this.type_ === 'test') { 376e41f4b71Sopenharmony_ci ... 377e41f4b71Sopenharmony_ci } else if (this.type_ === 'test/video') { 378e41f4b71Sopenharmony_ci ... 379e41f4b71Sopenharmony_ci } else { 380e41f4b71Sopenharmony_ci // other 381e41f4b71Sopenharmony_ci } 382e41f4b71Sopenharmony_ci ... 383e41f4b71Sopenharmony_ci } 384e41f4b71Sopenharmony_ci ... 385e41f4b71Sopenharmony_ci } 386e41f4b71Sopenharmony_ci ... 387e41f4b71Sopenharmony_ci 388e41f4b71Sopenharmony_ci build(){ 389e41f4b71Sopenharmony_ci ... 390e41f4b71Sopenharmony_ci Stack() { 391e41f4b71Sopenharmony_ci ... 392e41f4b71Sopenharmony_ci Web({ src: $rawfile("test.html"), controller: this.browserTabController }) 393e41f4b71Sopenharmony_ci // Enable same-layer rendering. 394e41f4b71Sopenharmony_ci .enableNativeEmbedMode(true) 395e41f4b71Sopenharmony_ci // Register the same-layer tag of "object" and type of "test." 396e41f4b71Sopenharmony_ci .registerNativeEmbedRule("object", "test") 397e41f4b71Sopenharmony_ci ... 398e41f4b71Sopenharmony_ci } 399e41f4b71Sopenharmony_ci ... 400e41f4b71Sopenharmony_ci } 401e41f4b71Sopenharmony_ci 402e41f4b71Sopenharmony_ci ``` 403e41f4b71Sopenharmony_ci 404e41f4b71Sopenharmony_ci- Example of using **registerNativeEmbedRule** on the frontend page, with the tag of "object" and type of "test": 405e41f4b71Sopenharmony_ci 406e41f4b71Sopenharmony_ci ```html 407e41f4b71Sopenharmony_ci 408e41f4b71Sopenharmony_ci <!DOCTYPE html> 409e41f4b71Sopenharmony_ci <html> 410e41f4b71Sopenharmony_ci <head> 411e41f4b71Sopenharmony_ci <title>Same-layer rendering test html</title> 412e41f4b71Sopenharmony_ci <meta name="viewport"> 413e41f4b71Sopenharmony_ci </head> 414e41f4b71Sopenharmony_ci <body> 415e41f4b71Sopenharmony_ci <div> 416e41f4b71Sopenharmony_ci <div> 417e41f4b71Sopenharmony_ci <object id="nativeButton" type="test" width="800" height="800" data="test?params1=xxx?" style = "background-color:red"/> 418e41f4b71Sopenharmony_ci <param name="id" value="playerId" /> 419e41f4b71Sopenharmony_ci <param name="data" value='{}' /> 420e41f4b71Sopenharmony_ci </object> 421e41f4b71Sopenharmony_ci </div> 422e41f4b71Sopenharmony_ci <div> 423e41f4b71Sopenharmony_ci <object id="nativeVideo" type="test/video" width="500" height="500" data="test" style = "background-color:red"/><object> 424e41f4b71Sopenharmony_ci </div> 425e41f4b71Sopenharmony_ci </div> 426e41f4b71Sopenharmony_ci <div id="button" width="500" height="200"> 427e41f4b71Sopenharmony_ci <p>bottom</p> 428e41f4b71Sopenharmony_ci </div> 429e41f4b71Sopenharmony_ci 430e41f4b71Sopenharmony_ci </body> 431e41f4b71Sopenharmony_ci </html> 432e41f4b71Sopenharmony_ci ``` 433e41f4b71Sopenharmony_ci 434e41f4b71Sopenharmony_ci## Drawing the TextInput Component and Synchronizing Position Information Returned During Same-Layer Element Updates to the Component 435e41f4b71Sopenharmony_ci 436e41f4b71Sopenharmony_ciThe same-layer elements are updated as a result of scrolling, scaling, or any other behavior that may cause a re-layout. The positions of same-layer elements are based on the **\<Web>** component coordinate system. For web page scaling that does not change the element size, only the position changes, and the width and height remain at the initial values. 437e41f4b71Sopenharmony_ci 438e41f4b71Sopenharmony_ciFor components that require location information, such as **\<TextInput>** and **\<TextArea>**, you need to synchronize the location information reported by the same-layer elements to the components in real time. 439e41f4b71Sopenharmony_ci 440e41f4b71Sopenharmony_ci- Complete sample code on the application side: 441e41f4b71Sopenharmony_ci 442e41f4b71Sopenharmony_ci ```ts 443e41f4b71Sopenharmony_ci ... 444e41f4b71Sopenharmony_ci class MyNodeController extends NodeController { 445e41f4b71Sopenharmony_ci ... 446e41f4b71Sopenharmony_ci makeNode(uiContext: UIContext): FrameNode | null{ 447e41f4b71Sopenharmony_ci 448e41f4b71Sopenharmony_ci if (this.type_ === 'application/view') { 449e41f4b71Sopenharmony_ci this.rootNode.build(wrapBuilder(TextInputBuilder), { 450e41f4b71Sopenharmony_ci textOne: "myInput", 451e41f4b71Sopenharmony_ci width: this.width_, 452e41f4b71Sopenharmony_ci height: this.height_ 453e41f4b71Sopenharmony_ci }); 454e41f4b71Sopenharmony_ci } else { 455e41f4b71Sopenharmony_ci // other 456e41f4b71Sopenharmony_ci } 457e41f4b71Sopenharmony_ci ... 458e41f4b71Sopenharmony_ci } 459e41f4b71Sopenharmony_ci ... 460e41f4b71Sopenharmony_ci } 461e41f4b71Sopenharmony_ci 462e41f4b71Sopenharmony_ci 463e41f4b71Sopenharmony_ci @Component 464e41f4b71Sopenharmony_ci struct TextInputComponent { 465e41f4b71Sopenharmony_ci @Prop params: Params 466e41f4b71Sopenharmony_ci @State bkColor: Color = Color.Red 467e41f4b71Sopenharmony_ci mXComponentController: XComponentController = new XComponentController(); 468e41f4b71Sopenharmony_ci 469e41f4b71Sopenharmony_ci build() { 470e41f4b71Sopenharmony_ci Column() { 471e41f4b71Sopenharmony_ci TextInput({ text: `${this.params.textOne}` }) 472e41f4b71Sopenharmony_ci .height(50) 473e41f4b71Sopenharmony_ci .width(200) 474e41f4b71Sopenharmony_ci .backgroundColor(Color.Green) 475e41f4b71Sopenharmony_ci .onTouch((event) => { 476e41f4b71Sopenharmony_ci console.log('input1 event ' + JSON.stringify(event)); 477e41f4b71Sopenharmony_ci }).margin({ top: 30}) 478e41f4b71Sopenharmony_ci 479e41f4b71Sopenharmony_ci TextInput({ text: `${this.params.textOne}` }) 480e41f4b71Sopenharmony_ci .height(50) 481e41f4b71Sopenharmony_ci .width(200) 482e41f4b71Sopenharmony_ci .backgroundColor(Color.Green) 483e41f4b71Sopenharmony_ci .onTouch((event) => { 484e41f4b71Sopenharmony_ci console.log('input2 event ' + JSON.stringify(event)); 485e41f4b71Sopenharmony_ci }).margin({ top: 30}) 486e41f4b71Sopenharmony_ci 487e41f4b71Sopenharmony_ci TextInput({ text: `${this.params.textOne}` }) 488e41f4b71Sopenharmony_ci .height(50) 489e41f4b71Sopenharmony_ci .width(200) 490e41f4b71Sopenharmony_ci .backgroundColor(Color.Green) 491e41f4b71Sopenharmony_ci .onTouch((event) => { 492e41f4b71Sopenharmony_ci console.log('input2 event ' + JSON.stringify(event)); 493e41f4b71Sopenharmony_ci }).margin({ top: 30}) 494e41f4b71Sopenharmony_ci } 495e41f4b71Sopenharmony_ci .width(this.params.width) 496e41f4b71Sopenharmony_ci .height(this.params.height) 497e41f4b71Sopenharmony_ci } 498e41f4b71Sopenharmony_ci } 499e41f4b71Sopenharmony_ci 500e41f4b71Sopenharmony_ci @Builder 501e41f4b71Sopenharmony_ci function TextInputBuilder(params: Params) { 502e41f4b71Sopenharmony_ci TextInputComponent({ params: params }) 503e41f4b71Sopenharmony_ci .height(params.height) 504e41f4b71Sopenharmony_ci .width(params.width) 505e41f4b71Sopenharmony_ci .backgroundColor(Color.Red) 506e41f4b71Sopenharmony_ci } 507e41f4b71Sopenharmony_ci 508e41f4b71Sopenharmony_ci @Entry 509e41f4b71Sopenharmony_ci @Component 510e41f4b71Sopenharmony_ci struct Page { 511e41f4b71Sopenharmony_ci browserTabController: WebviewController = new webview.WebviewController() 512e41f4b71Sopenharmony_ci private nodeControllerMap: Map<string, MyNodeController> = new Map(); 513e41f4b71Sopenharmony_ci @State componentIdArr: Array<string> = []; 514e41f4b71Sopenharmony_ci @State edges: Edges = {}; 515e41f4b71Sopenharmony_ci 516e41f4b71Sopenharmony_ci build() { 517e41f4b71Sopenharmony_ci Row() { 518e41f4b71Sopenharmony_ci Column() { 519e41f4b71Sopenharmony_ci Stack(){ 520e41f4b71Sopenharmony_ci ForEach(this.componentIdArr, (componentId: string) => { 521e41f4b71Sopenharmony_ci NodeContainer(this.nodeControllerMap.get(componentId)).position(this.edges) 522e41f4b71Sopenharmony_ci }, (embedId: string) => embedId) 523e41f4b71Sopenharmony_ci 524e41f4b71Sopenharmony_ci Web({ src: $rawfile('test.html'), controller: this.browserTabController}) 525e41f4b71Sopenharmony_ci .enableNativeEmbedMode(true) 526e41f4b71Sopenharmony_ci .registerNativeEmbedRule("object", "APPlication/view") 527e41f4b71Sopenharmony_ci .onNativeEmbedLifecycleChange((embed) => { 528e41f4b71Sopenharmony_ci const componentId = embed.info?.id?.toString() as string; 529e41f4b71Sopenharmony_ci if (embed.status == NativeEmbedStatus.CREATE) { 530e41f4b71Sopenharmony_ci // You are advised to use position in edges mode to avoid extra precision loss caused by floating-point calculation during the conversion between px and vp. 531e41f4b71Sopenharmony_ci this.edges = {left: `${embed.info?.position?.x as number}px`, top: `${embed.info?.position?.y as number}px`} 532e41f4b71Sopenharmony_ci let nodeController = new MyNodeController() 533e41f4b71Sopenharmony_ci nodeController.setRenderOption({surfaceId : embed.surfaceId as string, 534e41f4b71Sopenharmony_ci type : embed.info?.type as string, 535e41f4b71Sopenharmony_ci renderType : NodeRenderType.RENDER_TYPE_TEXTURE, 536e41f4b71Sopenharmony_ci embedId : embed.embedId as string, 537e41f4b71Sopenharmony_ci width : px2vp(embed.info?.width), 538e41f4b71Sopenharmony_ci height :px2vp(embed.info?.height)}) 539e41f4b71Sopenharmony_ci nodeController.rebuild() 540e41f4b71Sopenharmony_ci 541e41f4b71Sopenharmony_ci this.nodeControllerMap.set(componentId, nodeController) 542e41f4b71Sopenharmony_ci this.componentIdArr.push(componentId) 543e41f4b71Sopenharmony_ci } else if (embed.status == NativeEmbedStatus.UPDATE) { 544e41f4b71Sopenharmony_ci console.log("NativeEmbed update" + JSON.stringify(embed.info)) 545e41f4b71Sopenharmony_ci 546e41f4b71Sopenharmony_ci this.edges = {left: `${embed.info?.position?.x as number}px`, top: `${embed.info?.position?.y as number}px`} 547e41f4b71Sopenharmony_ci let nodeController = this.nodeControllerMap.get(componentId) 548e41f4b71Sopenharmony_ci 549e41f4b71Sopenharmony_ci nodeController?.updateNode({text: 'update', width : px2vp(embed.info?.width), 550e41f4b71Sopenharmony_ci height :px2vp(embed.info?.height)} as ESObject) 551e41f4b71Sopenharmony_ci nodeController?.rebuild() 552e41f4b71Sopenharmony_ci } else { 553e41f4b71Sopenharmony_ci let nodeController = this.nodeControllerMap.get(componentId) 554e41f4b71Sopenharmony_ci nodeController?.setBuilderNode(null) 555e41f4b71Sopenharmony_ci nodeController?.rebuild() 556e41f4b71Sopenharmony_ci } 557e41f4b71Sopenharmony_ci }) 558e41f4b71Sopenharmony_ci .onNativeEmbedGestureEvent((touch) => { 559e41f4b71Sopenharmony_ci this.componentIdArr.forEach((componentId: string) => { 560e41f4b71Sopenharmony_ci let nodeController = this.nodeControllerMap.get(componentId) 561e41f4b71Sopenharmony_ci if (nodeController?.getEmbedId() === touch.embedId) { 562e41f4b71Sopenharmony_ci let ret = nodeController?.postEvent(touch.touchEvent) 563e41f4b71Sopenharmony_ci if (ret) { 564e41f4b71Sopenharmony_ci console.log("onNativeEmbedGestureEvent success " + componentId) 565e41f4b71Sopenharmony_ci } else { 566e41f4b71Sopenharmony_ci console.log("onNativeEmbedGestureEvent fail " + componentId) 567e41f4b71Sopenharmony_ci } 568e41f4b71Sopenharmony_ci } 569e41f4b71Sopenharmony_ci }) 570e41f4b71Sopenharmony_ci }) 571e41f4b71Sopenharmony_ci } 572e41f4b71Sopenharmony_ci } 573e41f4b71Sopenharmony_ci .width('100%') 574e41f4b71Sopenharmony_ci } 575e41f4b71Sopenharmony_ci .height('100%') 576e41f4b71Sopenharmony_ci } 577e41f4b71Sopenharmony_ci } 578e41f4b71Sopenharmony_ci 579e41f4b71Sopenharmony_ci ``` 580e41f4b71Sopenharmony_ci 581e41f4b71Sopenharmony_ci- Example of the frontend page: 582e41f4b71Sopenharmony_ci 583e41f4b71Sopenharmony_ci ```html 584e41f4b71Sopenharmony_ci <!DOCTYPE html> 585e41f4b71Sopenharmony_ci <html> 586e41f4b71Sopenharmony_ci <head> 587e41f4b71Sopenharmony_ci <title>Same-layer rendering test html</title> 588e41f4b71Sopenharmony_ci <meta charset="UTF-8"> 589e41f4b71Sopenharmony_ci <style> 590e41f4b71Sopenharmony_ci html { 591e41f4b71Sopenharmony_ci background-color: blue; 592e41f4b71Sopenharmony_ci } 593e41f4b71Sopenharmony_ci </style> 594e41f4b71Sopenharmony_ci </head> 595e41f4b71Sopenharmony_ci <body> 596e41f4b71Sopenharmony_ci 597e41f4b71Sopenharmony_ci <div id="bodyId" style="width:800px; height:1000px; margin-top:1000px;"> 598e41f4b71Sopenharmony_ci <object id="cameraTest" type="application/view" width="100%" height="100%" ></object> 599e41f4b71Sopenharmony_ci </div> 600e41f4b71Sopenharmony_ci <div style="height:1000px;"> 601e41f4b71Sopenharmony_ci </div> 602e41f4b71Sopenharmony_ci 603e41f4b71Sopenharmony_ci </body> 604e41f4b71Sopenharmony_ci </html> 605e41f4b71Sopenharmony_ci ``` 606