1e41f4b71Sopenharmony_ci# Taking Over the Media Playback on Web Pages 2e41f4b71Sopenharmony_ci 3e41f4b71Sopenharmony_ciThe **Web** component provides the capability for applications to take over web media player, so that applications can better enhance web media playback capabilities (for example, image quality). 4e41f4b71Sopenharmony_ci 5e41f4b71Sopenharmony_ci## When to Use 6e41f4b71Sopenharmony_ci 7e41f4b71Sopenharmony_ciThere are unsatisfactory scenarios when media is played on a web page, such as unclear video quality, overly simplistic and feature-lacking web player interface, or even videos that cannot be played. 8e41f4b71Sopenharmony_ci 9e41f4b71Sopenharmony_ciIn this case, if you want to improve the web media playback experience by taking over web media playback with your own or third-party players, you can utilize this functionality. 10e41f4b71Sopenharmony_ci 11e41f4b71Sopenharmony_ci## Implementation Principle 12e41f4b71Sopenharmony_ci 13e41f4b71Sopenharmony_ci### Framework for the ArkWeb Kernel to Play Media 14e41f4b71Sopenharmony_ci 15e41f4b71Sopenharmony_ciWhen this functionality is disabled, the playback architecture of the ArkWeb kernel is as follows: 16e41f4b71Sopenharmony_ci 17e41f4b71Sopenharmony_ci  18e41f4b71Sopenharmony_ci 19e41f4b71Sopenharmony_ci > **NOTE** 20e41f4b71Sopenharmony_ci > - In the preceding figure, step 1 indicates that the ArkWeb kernel creates a **WebMdiaPlayer** to play media resources on web pages. 21e41f4b71Sopenharmony_ci > - Step 2 indicates that the **WebMdiaPlayer** uses the system decoder to render media data. 22e41f4b71Sopenharmony_ci 23e41f4b71Sopenharmony_ciWhen this functionality is enabled, the playback architecture of the ArkWeb kernel is as follows: 24e41f4b71Sopenharmony_ci 25e41f4b71Sopenharmony_ci  26e41f4b71Sopenharmony_ci 27e41f4b71Sopenharmony_ci > **NOTE** 28e41f4b71Sopenharmony_ci > - In the preceding figure, step 1 indicates that the ArkWeb kernel creates a **WebMdiaPlayer** to play media resources on web pages. 29e41f4b71Sopenharmony_ci > - Step 2 indicates that the **WebMdiaPlayer** uses the **NativeMediaPlayer** provided by the application to render media data. 30e41f4b71Sopenharmony_ci 31e41f4b71Sopenharmony_ci 32e41f4b71Sopenharmony_ci### Interactions Between the ArkWeb Kernel and Application 33e41f4b71Sopenharmony_ci 34e41f4b71Sopenharmony_ci  35e41f4b71Sopenharmony_ci 36e41f4b71Sopenharmony_ci > **NOTE** 37e41f4b71Sopenharmony_ci > - For details about step 1 in the preceding figure, see [Enabling Web Media Playback Takeover](#enabling-web-media-playback-takeover). 38e41f4b71Sopenharmony_ci > - For details about step 2, see [Creating a Native Media Player](#creating-a-native-media-player). 39e41f4b71Sopenharmony_ci > - For details about step 3, see [Drawing Native Media Player Components](#drawing-native-media-player-components). 40e41f4b71Sopenharmony_ci > - For details about step 4, see [Executing Playback Control Commands Sent by ArkWeb Kernel to the Native Media Player](#executing-playback-control-commands-sent-by-arkweb-kernel-to-the-native-media-player). 41e41f4b71Sopenharmony_ci > - For details about step 5, see [Notifying the State Information of Native Media Player to the ArkWeb Kernel](#notifying-the-state-information-of-native-media-player-to-the-arkweb-kernel). 42e41f4b71Sopenharmony_ci 43e41f4b71Sopenharmony_ci## How to Develop 44e41f4b71Sopenharmony_ci 45e41f4b71Sopenharmony_ci### Enabling Web Media Playback Takeover 46e41f4b71Sopenharmony_ci 47e41f4b71Sopenharmony_ciTo take over web media playback, you need to enable this functionality 48e41f4b71Sopenharmony_ci 49e41f4b71Sopenharmony_ciusing [enableNativeMediaPlayer](../reference/apis-arkweb/ts-basic-components-web.md#enablenativemediaplayer12). 50e41f4b71Sopenharmony_ci 51e41f4b71Sopenharmony_ci ```ts 52e41f4b71Sopenharmony_ci // xxx.ets 53e41f4b71Sopenharmony_ci import { webview } from '@kit.ArkWeb'; 54e41f4b71Sopenharmony_ci 55e41f4b71Sopenharmony_ci @Entry 56e41f4b71Sopenharmony_ci @Component 57e41f4b71Sopenharmony_ci struct WebComponent { 58e41f4b71Sopenharmony_ci controller: webview.WebviewController = new webview.WebviewController(); 59e41f4b71Sopenharmony_ci 60e41f4b71Sopenharmony_ci build() { 61e41f4b71Sopenharmony_ci Column() { 62e41f4b71Sopenharmony_ci Web({ src: 'www.example.com', controller: this.controller }) 63e41f4b71Sopenharmony_ci .enableNativeMediaPlayer({ enable: true, shouldOverlay: false }) 64e41f4b71Sopenharmony_ci } 65e41f4b71Sopenharmony_ci } 66e41f4b71Sopenharmony_ci } 67e41f4b71Sopenharmony_ci ``` 68e41f4b71Sopenharmony_ci 69e41f4b71Sopenharmony_ciFor details about the API and parameters, see [enableNativeMediaPlayer](../reference/apis-arkweb/ts-basic-components-web.md#enablenativemediaplayer12). 70e41f4b71Sopenharmony_ci 71e41f4b71Sopenharmony_ci### Creating a Native Media Player 72e41f4b71Sopenharmony_ci 73e41f4b71Sopenharmony_ciOnce this functionality is enabled, the ArkWeb kernel triggers the callback function registered by [onCreateNativeMediaPlayer](../reference/apis-arkweb/js-apis-webview.md#oncreatenativemediaplayer12) each time a media file needs to be played on a web page. 74e41f4b71Sopenharmony_ci 75e41f4b71Sopenharmony_ciYou need to register a callback function for creating a native media player by invoking **onCreateNativeMediaPlayer**. 76e41f4b71Sopenharmony_ci 77e41f4b71Sopenharmony_ciThe callback function determines whether to create a native media player to take over the web page media resources based on the media information. 78e41f4b71Sopenharmony_ci 79e41f4b71Sopenharmony_ci * If the application does not take over the web page media resource, **null** is returned in the callback function. 80e41f4b71Sopenharmony_ci * If the application takes over the web page media resource, a native media player instance is returned in the callback function. 81e41f4b71Sopenharmony_ci 82e41f4b71Sopenharmony_ciThe native media player needs to implement the [NativeMediaPlayerBridge](../reference/apis-arkweb/js-apis-webview.md#nativemediaplayerbridge12) API so that the ArkWeb kernel can control the playback on the native media player. 83e41f4b71Sopenharmony_ci 84e41f4b71Sopenharmony_ci ```ts 85e41f4b71Sopenharmony_ci // xxx.ets 86e41f4b71Sopenharmony_ci import { webview } from '@kit.ArkWeb'; 87e41f4b71Sopenharmony_ci 88e41f4b71Sopenharmony_ci // Implement the webview.NativeMediaPlayerBridge API. 89e41f4b71Sopenharmony_ci // The ArkWeb kernel calls the APIs to control playback on NativeMediaPlayer. 90e41f4b71Sopenharmony_ci class NativeMediaPlayerImpl implements webview.NativeMediaPlayerBridge { 91e41f4b71Sopenharmony_ci // ...Implement the APIs in NativeMediaPlayerBridge... 92e41f4b71Sopenharmony_ci constructor(handler: webview.NativeMediaPlayerHandler, mediaInfo: webview.MediaInfo) {} 93e41f4b71Sopenharmony_ci updateRect(x: number, y: number, width: number, height: number) {} 94e41f4b71Sopenharmony_ci play() {} 95e41f4b71Sopenharmony_ci pause() {} 96e41f4b71Sopenharmony_ci seek(targetTime: number) {} 97e41f4b71Sopenharmony_ci release() {} 98e41f4b71Sopenharmony_ci setVolume(volume: number) {} 99e41f4b71Sopenharmony_ci setMuted(muted: boolean) {} 100e41f4b71Sopenharmony_ci setPlaybackRate(playbackRate: number) {} 101e41f4b71Sopenharmony_ci enterFullscreen() {} 102e41f4b71Sopenharmony_ci exitFullscreen() {} 103e41f4b71Sopenharmony_ci } 104e41f4b71Sopenharmony_ci 105e41f4b71Sopenharmony_ci @Entry 106e41f4b71Sopenharmony_ci @Component 107e41f4b71Sopenharmony_ci struct WebComponent { 108e41f4b71Sopenharmony_ci controller: webview.WebviewController = new webview.WebviewController(); 109e41f4b71Sopenharmony_ci 110e41f4b71Sopenharmony_ci build() { 111e41f4b71Sopenharmony_ci Column() { 112e41f4b71Sopenharmony_ci Web({ src: 'www.example.com', controller: this.controller }) 113e41f4b71Sopenharmony_ci .enableNativeMediaPlayer({ enable: true, shouldOverlay: false }) 114e41f4b71Sopenharmony_ci .onPageBegin((event) => { 115e41f4b71Sopenharmony_ci this.controller.onCreateNativeMediaPlayer((handler: webview.NativeMediaPlayerHandler, mediaInfo: webview.MediaInfo) => { 116e41f4b71Sopenharmony_ci // Determine whether to take over the media. 117e41f4b71Sopenharmony_ci if (!shouldHandle(mediaInfo)) { 118e41f4b71Sopenharmony_ci // The native media player does not take over the media. 119e41f4b71Sopenharmony_ci // Return null. The ArkWeb kernel will play the media with the web media player. 120e41f4b71Sopenharmony_ci return null; 121e41f4b71Sopenharmony_ci } 122e41f4b71Sopenharmony_ci // Take over the web media. 123e41f4b71Sopenharmony_ci // Return a native media player instance to the ArkWeb kernel. 124e41f4b71Sopenharmony_ci let nativePlayer: webview.NativeMediaPlayerBridge = new NativeMediaPlayerImpl(handler, mediaInfo); 125e41f4b71Sopenharmony_ci return nativePlayer; 126e41f4b71Sopenharmony_ci }); 127e41f4b71Sopenharmony_ci }) 128e41f4b71Sopenharmony_ci } 129e41f4b71Sopenharmony_ci } 130e41f4b71Sopenharmony_ci } 131e41f4b71Sopenharmony_ci 132e41f4b71Sopenharmony_ci // stub 133e41f4b71Sopenharmony_ci function shouldHandle(mediaInfo: webview.MediaInfo) { 134e41f4b71Sopenharmony_ci return true; 135e41f4b71Sopenharmony_ci } 136e41f4b71Sopenharmony_ci ``` 137e41f4b71Sopenharmony_ci 138e41f4b71Sopenharmony_ciFor details about the APIs and parameters, see [onCreateNativeMediaPlayer](../reference/apis-arkweb/js-apis-webview.md#oncreatenativemediaplayer12). 139e41f4b71Sopenharmony_ci 140e41f4b71Sopenharmony_ci### Drawing Native Media Player Components 141e41f4b71Sopenharmony_ci 142e41f4b71Sopenharmony_ciWhen the application takes over the media of the web page, you need to draw the native media player components and video images on the surface provided by the ArkWeb kernel. 143e41f4b71Sopenharmony_ci 144e41f4b71Sopenharmony_ciThen the ArkWeb kernel combines the surface with the web page and displays the web page on the screen. 145e41f4b71Sopenharmony_ci 146e41f4b71Sopenharmony_ciThis process is the same as that of [Rendering and Drawing XComponent+AVPlayer and Button Components at the Same Layer](web-same-layer.md#) 147e41f4b71Sopenharmony_ci 148e41f4b71Sopenharmony_ci 149e41f4b71Sopenharmony_ci1. Save the **UIContext** when the application is started, which will be used in the subsequent rendering and drawing at the same layer. 150e41f4b71Sopenharmony_ci 151e41f4b71Sopenharmony_ci ```ts 152e41f4b71Sopenharmony_ci // xxxAbility.ets 153e41f4b71Sopenharmony_ci 154e41f4b71Sopenharmony_ci import { UIAbility } from '@kit.AbilityKit'; 155e41f4b71Sopenharmony_ci import { window } from '@kit.ArkUI'; 156e41f4b71Sopenharmony_ci 157e41f4b71Sopenharmony_ci export default class EntryAbility extends UIAbility { 158e41f4b71Sopenharmony_ci onWindowStageCreate(windowStage: window.WindowStage): void { 159e41f4b71Sopenharmony_ci windowStage.loadContent('pages/Index', (err, data) => { 160e41f4b71Sopenharmony_ci if (err.code) { 161e41f4b71Sopenharmony_ci return; 162e41f4b71Sopenharmony_ci } 163e41f4b71Sopenharmony_ci // Save the UIContext, which will be used in subsequent rendering and drawing at the same layer. 164e41f4b71Sopenharmony_ci AppStorage.setOrCreate<UIContext>("UIContext", windowStage.getMainWindowSync().getUIContext()); 165e41f4b71Sopenharmony_ci }); 166e41f4b71Sopenharmony_ci } 167e41f4b71Sopenharmony_ci 168e41f4b71Sopenharmony_ci // ...Other APIs that need to be overridden... 169e41f4b71Sopenharmony_ci } 170e41f4b71Sopenharmony_ci ``` 171e41f4b71Sopenharmony_ci 172e41f4b71Sopenharmony_ci2. Use the surface created by the ArkWeb kernel is for rendering and drawing at the same layer. 173e41f4b71Sopenharmony_ci 174e41f4b71Sopenharmony_ci ```ts 175e41f4b71Sopenharmony_ci // xxx.ets 176e41f4b71Sopenharmony_ci import { webview } from '@kit.ArkWeb'; 177e41f4b71Sopenharmony_ci import { BuilderNode, FrameNode, NodeController, NodeRenderType } from '@kit.ArkUI'; 178e41f4b71Sopenharmony_ci 179e41f4b71Sopenharmony_ci interface ComponentParams {} 180e41f4b71Sopenharmony_ci 181e41f4b71Sopenharmony_ci class MyNodeController extends NodeController { 182e41f4b71Sopenharmony_ci private rootNode: BuilderNode<[ComponentParams]> | undefined; 183e41f4b71Sopenharmony_ci 184e41f4b71Sopenharmony_ci constructor(surfaceId: string, renderType: NodeRenderType) { 185e41f4b71Sopenharmony_ci super(); 186e41f4b71Sopenharmony_ci 187e41f4b71Sopenharmony_ci // Obtain the saved UIContext. 188e41f4b71Sopenharmony_ci let uiContext = AppStorage.get<UIContext>("UIContext"); 189e41f4b71Sopenharmony_ci this.rootNode = new BuilderNode(uiContext as UIContext, { surfaceId: surfaceId, type: renderType }); 190e41f4b71Sopenharmony_ci } 191e41f4b71Sopenharmony_ci 192e41f4b71Sopenharmony_ci makeNode(uiContext: UIContext): FrameNode | null { 193e41f4b71Sopenharmony_ci if (this.rootNode) { 194e41f4b71Sopenharmony_ci return this.rootNode.getFrameNode() as FrameNode; 195e41f4b71Sopenharmony_ci } 196e41f4b71Sopenharmony_ci return null; 197e41f4b71Sopenharmony_ci } 198e41f4b71Sopenharmony_ci 199e41f4b71Sopenharmony_ci build() { 200e41f4b71Sopenharmony_ci // Construct the native media player component. 201e41f4b71Sopenharmony_ci } 202e41f4b71Sopenharmony_ci } 203e41f4b71Sopenharmony_ci 204e41f4b71Sopenharmony_ci @Entry 205e41f4b71Sopenharmony_ci @Component 206e41f4b71Sopenharmony_ci struct WebComponent { 207e41f4b71Sopenharmony_ci node_controller?: MyNodeController; 208e41f4b71Sopenharmony_ci controller: webview.WebviewController = new webview.WebviewController(); 209e41f4b71Sopenharmony_ci @State show_native_media_player: boolean = false; 210e41f4b71Sopenharmony_ci 211e41f4b71Sopenharmony_ci build() { 212e41f4b71Sopenharmony_ci Column() { 213e41f4b71Sopenharmony_ci Stack({ alignContent: Alignment.TopStart }) { 214e41f4b71Sopenharmony_ci if (this.show_native_media_player) { 215e41f4b71Sopenharmony_ci NodeContainer(this.node_controller) 216e41f4b71Sopenharmony_ci .width(300) 217e41f4b71Sopenharmony_ci .height(150) 218e41f4b71Sopenharmony_ci .backgroundColor(Color.Transparent) 219e41f4b71Sopenharmony_ci .border({ width: 2, color: Color.Orange }) 220e41f4b71Sopenharmony_ci } 221e41f4b71Sopenharmony_ci Web({ src: 'www.example.com', controller: this.controller }) 222e41f4b71Sopenharmony_ci .enableNativeMediaPlayer({ enable: true, shouldOverlay: false }) 223e41f4b71Sopenharmony_ci .onPageBegin((event) => { 224e41f4b71Sopenharmony_ci this.controller.onCreateNativeMediaPlayer((handler: webview.NativeMediaPlayerHandler, mediaInfo: webview.MediaInfo) => { 225e41f4b71Sopenharmony_ci // Take over the web media. 226e41f4b71Sopenharmony_ci 227e41f4b71Sopenharmony_ci // Use the surface provided by the rendering at the same layer to construct a native media player component. 228e41f4b71Sopenharmony_ci this.node_controller = new MyNodeController(mediaInfo..surfaceInfo.id, NodeRenderType. RENDER_TYPE_TEXTURE); 229e41f4b71Sopenharmony_ci this.node_controller.build(); 230e41f4b71Sopenharmony_ci 231e41f4b71Sopenharmony_ci // Show the native media player component. 232e41f4b71Sopenharmony_ci this.show_native_media_player = true; 233e41f4b71Sopenharmony_ci 234e41f4b71Sopenharmony_ci // Return a native media player instance to the ArkWeb kernel. 235e41f4b71Sopenharmony_ci return null; 236e41f4b71Sopenharmony_ci }); 237e41f4b71Sopenharmony_ci }) 238e41f4b71Sopenharmony_ci } 239e41f4b71Sopenharmony_ci } 240e41f4b71Sopenharmony_ci } 241e41f4b71Sopenharmony_ci } 242e41f4b71Sopenharmony_ci ``` 243e41f4b71Sopenharmony_ci 244e41f4b71Sopenharmony_ciFor details about how to dynamically create components and draw them on the surface, see [Rendering and Drawing XComponent+AVPlayer and Button Components at the Same Layer](web-same-layer.md#). 245e41f4b71Sopenharmony_ci 246e41f4b71Sopenharmony_ci### Executing Playback Control Commands Sent by ArkWeb Kernel to the Native Media Player 247e41f4b71Sopenharmony_ci 248e41f4b71Sopenharmony_ciTo facilitate the control over native media player by the ArkWeb kernel, you need to implement the [NativeMediaPlayerBridge](../reference/apis-arkweb/js-apis-webview.md#nativemediaplayerbridge12) API on the native media player and perform operations on the native media player based on the functionality of each API. 249e41f4b71Sopenharmony_ci 250e41f4b71Sopenharmony_ci ```ts 251e41f4b71Sopenharmony_ci // xxx.ets 252e41f4b71Sopenharmony_ci import { webview } from '@kit.ArkWeb'; 253e41f4b71Sopenharmony_ci 254e41f4b71Sopenharmony_ci class ActualNativeMediaPlayerListener { 255e41f4b71Sopenharmony_ci constructor(handler: webview.NativeMediaPlayerHandler) {} 256e41f4b71Sopenharmony_ci } 257e41f4b71Sopenharmony_ci 258e41f4b71Sopenharmony_ci class NativeMediaPlayerImpl implements webview.NativeMediaPlayerBridge { 259e41f4b71Sopenharmony_ci constructor(handler: webview.NativeMediaPlayerHandler, mediaInfo: webview.MediaInfo) { 260e41f4b71Sopenharmony_ci // 1. Create a listener for the native media player. 261e41f4b71Sopenharmony_ci let listener: ActualNativeMediaPlayerListener = new ActualNativeMediaPlayerListener(handler); 262e41f4b71Sopenharmony_ci // 2. Create a native media player. 263e41f4b71Sopenharmony_ci // 3. Listen for the local player. 264e41f4b71Sopenharmony_ci // ... 265e41f4b71Sopenharmony_ci } 266e41f4b71Sopenharmony_ci 267e41f4b71Sopenharmony_ci updateRect(x: number, y: number, width: number, height: number) { 268e41f4b71Sopenharmony_ci // The position and size of the <video> tag are changed. 269e41f4b71Sopenharmony_ci // Make changes based on the information change. 270e41f4b71Sopenharmony_ci } 271e41f4b71Sopenharmony_ci 272e41f4b71Sopenharmony_ci play() { 273e41f4b71Sopenharmony_ci // Starts the native media player for playback. 274e41f4b71Sopenharmony_ci } 275e41f4b71Sopenharmony_ci 276e41f4b71Sopenharmony_ci pause() { 277e41f4b71Sopenharmony_ci // Pause the playback. 278e41f4b71Sopenharmony_ci } 279e41f4b71Sopenharmony_ci 280e41f4b71Sopenharmony_ci seek(targetTime: number) { 281e41f4b71Sopenharmony_ci // The native media player seeks to the target playback time. 282e41f4b71Sopenharmony_ci } 283e41f4b71Sopenharmony_ci 284e41f4b71Sopenharmony_ci release() { 285e41f4b71Sopenharmony_ci // Destroy the native media player. 286e41f4b71Sopenharmony_ci } 287e41f4b71Sopenharmony_ci 288e41f4b71Sopenharmony_ci setVolume(volume: number) { 289e41f4b71Sopenharmony_ci // The ArkWeb kernel adjusts the volume of the native media player. 290e41f4b71Sopenharmony_ci // Set the volume of the native media player. 291e41f4b71Sopenharmony_ci } 292e41f4b71Sopenharmony_ci 293e41f4b71Sopenharmony_ci setMuted(muted: boolean) { 294e41f4b71Sopenharmony_ci // Mute or unmute the native media player. 295e41f4b71Sopenharmony_ci } 296e41f4b71Sopenharmony_ci 297e41f4b71Sopenharmony_ci setPlaybackRate(playbackRate: number) { 298e41f4b71Sopenharmony_ci // Set the playback rate of the native media player. 299e41f4b71Sopenharmony_ci } 300e41f4b71Sopenharmony_ci 301e41f4b71Sopenharmony_ci enterFullscreen() { 302e41f4b71Sopenharmony_ci // Enter the full-screen mode. 303e41f4b71Sopenharmony_ci } 304e41f4b71Sopenharmony_ci 305e41f4b71Sopenharmony_ci exitFullscreen() { 306e41f4b71Sopenharmony_ci // Exit the full-screen mode. 307e41f4b71Sopenharmony_ci } 308e41f4b71Sopenharmony_ci } 309e41f4b71Sopenharmony_ci ``` 310e41f4b71Sopenharmony_ci 311e41f4b71Sopenharmony_ciFor details about the APIs, see [NativeMediaPlayerBridge](../reference/apis-arkweb/js-apis-webview.md#nativemediaplayerbridge12). 312e41f4b71Sopenharmony_ci 313e41f4b71Sopenharmony_ci### Notifying the State Information of Native Media Player to the ArkWeb Kernel 314e41f4b71Sopenharmony_ci 315e41f4b71Sopenharmony_ciThe ArkWeb kernel requires the state information of the local player to update to the web page, such as the width and height of the video, playback time, and cache time. You need to notify the ArkWeb kernel of the state information of the native media player. 316e41f4b71Sopenharmony_ci 317e41f4b71Sopenharmony_ciThrough the [onCreateNativeMediaPlayer](../reference/apis-arkweb/js-apis-webview.md#oncreatenativemediaplayer12) API, the Ark Web kernel passes a [NativeMediaPlayerHandler](../reference/apis-arkweb/js-apis-webview.md#nativemediaplayerhandler12) object to the application. 318e41f4b71Sopenharmony_ci 319e41f4b71Sopenharmony_ciYou need to use this object to notify the ArkWeb kernel of the latest state information of the native media player. 320e41f4b71Sopenharmony_ci 321e41f4b71Sopenharmony_ci ```ts 322e41f4b71Sopenharmony_ci // xxx.ets 323e41f4b71Sopenharmony_ci import { webview } from '@kit.ArkWeb'; 324e41f4b71Sopenharmony_ci 325e41f4b71Sopenharmony_ci class ActualNativeMediaPlayerListener { 326e41f4b71Sopenharmony_ci handler: webview.NativeMediaPlayerHandler; 327e41f4b71Sopenharmony_ci 328e41f4b71Sopenharmony_ci constructor(handler: webview.NativeMediaPlayerHandler) { 329e41f4b71Sopenharmony_ci this.handler = handler; 330e41f4b71Sopenharmony_ci } 331e41f4b71Sopenharmony_ci 332e41f4b71Sopenharmony_ci onPlaying() { 333e41f4b71Sopenharmony_ci // The native media player starts playback. 334e41f4b71Sopenharmony_ci this.handler.handleStatusChanged(webview.PlaybackStatus.PLAYING); 335e41f4b71Sopenharmony_ci } 336e41f4b71Sopenharmony_ci onPaused() { 337e41f4b71Sopenharmony_ci // The native media player pauses the playback. 338e41f4b71Sopenharmony_ci this.handler.handleStatusChanged(webview.PlaybackStatus.PAUSED); 339e41f4b71Sopenharmony_ci } 340e41f4b71Sopenharmony_ci onSeeking() { 341e41f4b71Sopenharmony_ci // The native media player starts to seek the target time point. 342e41f4b71Sopenharmony_ci this.handler.handleSeeking(); 343e41f4b71Sopenharmony_ci } 344e41f4b71Sopenharmony_ci onSeekDone() { 345e41f4b71Sopenharmony_ci // The target time point is sought. 346e41f4b71Sopenharmony_ci this.handler.handleSeekFinished(); 347e41f4b71Sopenharmony_ci } 348e41f4b71Sopenharmony_ci onEnded() { 349e41f4b71Sopenharmony_ci // The playback on the native media player is ended. 350e41f4b71Sopenharmony_ci this.handler.handleEnded(); 351e41f4b71Sopenharmony_ci } 352e41f4b71Sopenharmony_ci onVolumeChanged() { 353e41f4b71Sopenharmony_ci // Obtain the volume of the native media player. 354e41f4b71Sopenharmony_ci let volume: number = getVolume(); 355e41f4b71Sopenharmony_ci this.handler.handleVolumeChanged(volume); 356e41f4b71Sopenharmony_ci } 357e41f4b71Sopenharmony_ci onCurrentPlayingTimeUpdate() { 358e41f4b71Sopenharmony_ci // Update the playback time. 359e41f4b71Sopenharmony_ci let currentTime: number = getCurrentPlayingTime(); 360e41f4b71Sopenharmony_ci // Convert the time unit to second. 361e41f4b71Sopenharmony_ci let currentTimeInSeconds = convertToSeconds(currentTime); 362e41f4b71Sopenharmony_ci this.handler.handleTimeUpdate(currentTimeInSeconds); 363e41f4b71Sopenharmony_ci } 364e41f4b71Sopenharmony_ci onBufferedChanged() { 365e41f4b71Sopenharmony_ci // The buffer is changed. 366e41f4b71Sopenharmony_ci // Obtain the cache duration of the native media player. 367e41f4b71Sopenharmony_ci let bufferedEndTime: number = getCurrentBufferedTime(); 368e41f4b71Sopenharmony_ci // Convert the time unit to second. 369e41f4b71Sopenharmony_ci let bufferedEndTimeInSeconds = convertToSeconds(bufferedEndTime); 370e41f4b71Sopenharmony_ci this.handler.handleBufferedEndTimeChanged(bufferedEndTimeInSeconds); 371e41f4b71Sopenharmony_ci 372e41f4b71Sopenharmony_ci // Check the cache state. 373e41f4b71Sopenharmony_ci // If the cache state changes, notify the ArkWeb kernel. 374e41f4b71Sopenharmony_ci let lastReadyState: webview.ReadyState = getLastReadyState(); 375e41f4b71Sopenharmony_ci let currentReadyState: webview.ReadyState = getCurrentReadyState(); 376e41f4b71Sopenharmony_ci if (lastReadyState != currentReadyState) { 377e41f4b71Sopenharmony_ci this.handler.handleReadyStateChanged(currentReadyState); 378e41f4b71Sopenharmony_ci } 379e41f4b71Sopenharmony_ci } 380e41f4b71Sopenharmony_ci onEnterFullscreen() { 381e41f4b71Sopenharmony_ci // The native media player enters the full-screen mode. 382e41f4b71Sopenharmony_ci let isFullscreen: boolean = true; 383e41f4b71Sopenharmony_ci this.handler.handleFullscreenChanged(isFullscreen); 384e41f4b71Sopenharmony_ci } 385e41f4b71Sopenharmony_ci onExitFullscreen() { 386e41f4b71Sopenharmony_ci // The native media player exits the full-screen mode. 387e41f4b71Sopenharmony_ci let isFullscreen: boolean = false; 388e41f4b71Sopenharmony_ci this.handler.handleFullscreenChanged(isFullscreen); 389e41f4b71Sopenharmony_ci } 390e41f4b71Sopenharmony_ci onUpdateVideoSize(width: number, height: number) { 391e41f4b71Sopenharmony_ci // Notify the ArkWeb kernel when the local player parses the video width and height. 392e41f4b71Sopenharmony_ci this.handler.handleVideoSizeChanged(width, height); 393e41f4b71Sopenharmony_ci } 394e41f4b71Sopenharmony_ci 395e41f4b71Sopenharmony_ci // ...Listen for other state of the native media player. 396e41f4b71Sopenharmony_ci } 397e41f4b71Sopenharmony_ci @Entry 398e41f4b71Sopenharmony_ci @Component 399e41f4b71Sopenharmony_ci struct WebComponent { 400e41f4b71Sopenharmony_ci controller: webview.WebviewController = new webview.WebviewController(); 401e41f4b71Sopenharmony_ci @State show_native_media_player: boolean = false; 402e41f4b71Sopenharmony_ci 403e41f4b71Sopenharmony_ci build() { 404e41f4b71Sopenharmony_ci Column() { 405e41f4b71Sopenharmony_ci Web({ src: 'www.example.com', controller: this.controller }) 406e41f4b71Sopenharmony_ci .enableNativeMediaPlayer({enable: true, shouldOverlay: false}) 407e41f4b71Sopenharmony_ci .onPageBegin((event) => { 408e41f4b71Sopenharmony_ci this.controller.onCreateNativeMediaPlayer((handler: webview.NativeMediaPlayerHandler, mediaInfo: webview.MediaInfo) => { 409e41f4b71Sopenharmony_ci // Take over the web media. 410e41f4b71Sopenharmony_ci 411e41f4b71Sopenharmony_ci // Create a native media player instance. 412e41f4b71Sopenharmony_ci // let nativePlayer: NativeMediaPlayerImpl = new NativeMediaPlayerImpl(handler, mediaInfo); 413e41f4b71Sopenharmony_ci 414e41f4b71Sopenharmony_ci // Create a listener for the native media player state. 415e41f4b71Sopenharmony_ci let nativeMediaPlayerListener: ActualNativeMediaPlayerListener = new ActualNativeMediaPlayerListener(handler); 416e41f4b71Sopenharmony_ci // Listen for the native media player state. 417e41f4b71Sopenharmony_ci // nativePlayer.setListener(nativeMediaPlayerListener); 418e41f4b71Sopenharmony_ci 419e41f4b71Sopenharmony_ci // Return the native media player instance to the ArkWeb kernel. 420e41f4b71Sopenharmony_ci return null; 421e41f4b71Sopenharmony_ci }); 422e41f4b71Sopenharmony_ci }) 423e41f4b71Sopenharmony_ci } 424e41f4b71Sopenharmony_ci } 425e41f4b71Sopenharmony_ci } 426e41f4b71Sopenharmony_ci 427e41f4b71Sopenharmony_ci // stub 428e41f4b71Sopenharmony_ci function getVolume() { 429e41f4b71Sopenharmony_ci return 1; 430e41f4b71Sopenharmony_ci } 431e41f4b71Sopenharmony_ci function getCurrentPlayingTime() { 432e41f4b71Sopenharmony_ci return 1; 433e41f4b71Sopenharmony_ci } 434e41f4b71Sopenharmony_ci function getCurrentBufferedTime() { 435e41f4b71Sopenharmony_ci return 1; 436e41f4b71Sopenharmony_ci } 437e41f4b71Sopenharmony_ci function convertToSeconds(input: number) { 438e41f4b71Sopenharmony_ci return input; 439e41f4b71Sopenharmony_ci } 440e41f4b71Sopenharmony_ci function getLastReadyState() { 441e41f4b71Sopenharmony_ci return webview.ReadyState.HAVE_NOTHING; 442e41f4b71Sopenharmony_ci } 443e41f4b71Sopenharmony_ci function getCurrentReadyState() { 444e41f4b71Sopenharmony_ci return webview.ReadyState.HAVE_NOTHING; 445e41f4b71Sopenharmony_ci } 446e41f4b71Sopenharmony_ci ``` 447e41f4b71Sopenharmony_ci 448e41f4b71Sopenharmony_ci 449e41f4b71Sopenharmony_ciFor details about the APIs, see [NativeMediaPlayerHandler](../reference/apis-arkweb/js-apis-webview.md#nativemediaplayerhandler12). 450e41f4b71Sopenharmony_ci 451e41f4b71Sopenharmony_ci 452e41f4b71Sopenharmony_ci## Sample Code 453e41f4b71Sopenharmony_ci 454e41f4b71Sopenharmony_ci- Add the following permissions to **module.json5** before using it: 455e41f4b71Sopenharmony_ci 456e41f4b71Sopenharmony_ci ```ts 457e41f4b71Sopenharmony_ci "ohos.permission.INTERNET" 458e41f4b71Sopenharmony_ci ``` 459e41f4b71Sopenharmony_ci 460e41f4b71Sopenharmony_ci- Example of saving **UIContext** during application startup on the application side: 461e41f4b71Sopenharmony_ci 462e41f4b71Sopenharmony_ci ```ts 463e41f4b71Sopenharmony_ci // xxxAbility.ets 464e41f4b71Sopenharmony_ci 465e41f4b71Sopenharmony_ci import { UIAbility } from '@kit.AbilityKit'; 466e41f4b71Sopenharmony_ci import { window } from '@kit.ArkUI'; 467e41f4b71Sopenharmony_ci 468e41f4b71Sopenharmony_ci export default class EntryAbility extends UIAbility { 469e41f4b71Sopenharmony_ci onWindowStageCreate(windowStage: window.WindowStage): void { 470e41f4b71Sopenharmony_ci windowStage.loadContent('pages/Index', (err, data) => { 471e41f4b71Sopenharmony_ci if (err.code) { 472e41f4b71Sopenharmony_ci return; 473e41f4b71Sopenharmony_ci } 474e41f4b71Sopenharmony_ci // Save the UIContext, which will be used in subsequent rendering and drawing at the same layer. 475e41f4b71Sopenharmony_ci AppStorage.setOrCreate<UIContext>("UIContext", windowStage.getMainWindowSync().getUIContext()); 476e41f4b71Sopenharmony_ci }); 477e41f4b71Sopenharmony_ci } 478e41f4b71Sopenharmony_ci 479e41f4b71Sopenharmony_ci // ...Other APIs that need to be overridden... 480e41f4b71Sopenharmony_ci } 481e41f4b71Sopenharmony_ci ``` 482e41f4b71Sopenharmony_ci 483e41f4b71Sopenharmony_ci- Example of web media playback takeover: 484e41f4b71Sopenharmony_ci 485e41f4b71Sopenharmony_ci ```ts 486e41f4b71Sopenharmony_ci import { webview } from '@kit.ArkWeb'; 487e41f4b71Sopenharmony_ci import { BuilderNode, FrameNode, NodeController, NodeRenderType, UIContext } from '@kit.ArkUI'; 488e41f4b71Sopenharmony_ci import { AVPlayerDemo, AVPlayerListener } from './PlayerDemo'; 489e41f4b71Sopenharmony_ci 490e41f4b71Sopenharmony_ci // Implement the webview.NativeMediaPlayerBridge API. 491e41f4b71Sopenharmony_ci // The ArkWeb kernel calls the APIs to control playback on NativeMediaPlayer. 492e41f4b71Sopenharmony_ci class NativeMediaPlayerImpl implements webview.NativeMediaPlayerBridge { 493e41f4b71Sopenharmony_ci private surfaceId: string; 494e41f4b71Sopenharmony_ci mediaSource: string; 495e41f4b71Sopenharmony_ci private mediaHandler: webview.NativeMediaPlayerHandler; 496e41f4b71Sopenharmony_ci web: WebComponent; 497e41f4b71Sopenharmony_ci nativePlayer?: AVPlayerDemo; 498e41f4b71Sopenharmony_ci 499e41f4b71Sopenharmony_ci constructor(web: WebComponent, handler: webview.NativeMediaPlayerHandler, mediaInfo: webview.MediaInfo) { 500e41f4b71Sopenharmony_ci console.log('NativeMediaPlayerImpl.constructor, surface_id[' + mediaInfo.surfaceInfo.id + ']'); 501e41f4b71Sopenharmony_ci this.web = web; 502e41f4b71Sopenharmony_ci this.mediaHandler = handler; 503e41f4b71Sopenharmony_ci this.surfaceId = mediaInfo.surfaceInfo.id; 504e41f4b71Sopenharmony_ci this.mediaSource = mediaInfo.mediaSrcList[0].source; 505e41f4b71Sopenharmony_ci 506e41f4b71Sopenharmony_ci // Use the rendering function at the same layer to draw the video and its playback control components to the web page. 507e41f4b71Sopenharmony_ci this.web.node_controller = new MyNodeController( 508e41f4b71Sopenharmony_ci this.web, this.surfaceId, this.mediaHandler, this, NodeRenderType.RENDER_TYPE_TEXTURE) 509e41f4b71Sopenharmony_ci this.web.node_controller.build() 510e41f4b71Sopenharmony_ci this.web.show_native_media_player = true; 511e41f4b71Sopenharmony_ci 512e41f4b71Sopenharmony_ci console.log('NativeMediaPlayerImpl.mediaSource : ' + this.mediaSource); 513e41f4b71Sopenharmony_ci } 514e41f4b71Sopenharmony_ci 515e41f4b71Sopenharmony_ci setNativePlayer(nativePlayer: AVPlayerDemo) { 516e41f4b71Sopenharmony_ci this.nativePlayer = nativePlayer; 517e41f4b71Sopenharmony_ci } 518e41f4b71Sopenharmony_ci 519e41f4b71Sopenharmony_ci updateRect(x: number, y: number, width: number, height: number): void { 520e41f4b71Sopenharmony_ci this.web.node_width = width 521e41f4b71Sopenharmony_ci this.web.node_height = height 522e41f4b71Sopenharmony_ci } 523e41f4b71Sopenharmony_ci 524e41f4b71Sopenharmony_ci play() { 525e41f4b71Sopenharmony_ci this.nativePlayer?.play(); 526e41f4b71Sopenharmony_ci } 527e41f4b71Sopenharmony_ci pause() { 528e41f4b71Sopenharmony_ci this.nativePlayer?.pause(); 529e41f4b71Sopenharmony_ci } 530e41f4b71Sopenharmony_ci seek(targetTime: number) { 531e41f4b71Sopenharmony_ci } 532e41f4b71Sopenharmony_ci setVolume(volume: number) { 533e41f4b71Sopenharmony_ci } 534e41f4b71Sopenharmony_ci setMuted(muted: boolean) { 535e41f4b71Sopenharmony_ci } 536e41f4b71Sopenharmony_ci setPlaybackRate(playbackRate: number) { 537e41f4b71Sopenharmony_ci } 538e41f4b71Sopenharmony_ci release() { 539e41f4b71Sopenharmony_ci this.nativePlayer?.release(); 540e41f4b71Sopenharmony_ci this.web.show_native_media_player = false; 541e41f4b71Sopenharmony_ci } 542e41f4b71Sopenharmony_ci enterFullscreen() { 543e41f4b71Sopenharmony_ci } 544e41f4b71Sopenharmony_ci exitFullscreen() { 545e41f4b71Sopenharmony_ci } 546e41f4b71Sopenharmony_ci } 547e41f4b71Sopenharmony_ci 548e41f4b71Sopenharmony_ci // Listen for the NativeMediaPlayer state and report the state to the ArkWeb kernel using webview.NativeMediaPlayerHandler. 549e41f4b71Sopenharmony_ci class AVPlayerListenerImpl implements AVPlayerListener { 550e41f4b71Sopenharmony_ci handler: webview.NativeMediaPlayerHandler; 551e41f4b71Sopenharmony_ci component: NativePlayerComponent; 552e41f4b71Sopenharmony_ci 553e41f4b71Sopenharmony_ci constructor(handler: webview.NativeMediaPlayerHandler, component: NativePlayerComponent) { 554e41f4b71Sopenharmony_ci this.handler = handler; 555e41f4b71Sopenharmony_ci this.component = component; 556e41f4b71Sopenharmony_ci } 557e41f4b71Sopenharmony_ci onPlaying() { 558e41f4b71Sopenharmony_ci this.handler.handleStatusChanged(webview.PlaybackStatus.PLAYING); 559e41f4b71Sopenharmony_ci } 560e41f4b71Sopenharmony_ci onPaused() { 561e41f4b71Sopenharmony_ci this.handler.handleStatusChanged(webview.PlaybackStatus.PAUSED); 562e41f4b71Sopenharmony_ci } 563e41f4b71Sopenharmony_ci onDurationChanged(duration: number) { 564e41f4b71Sopenharmony_ci this.handler.handleDurationChanged(duration); 565e41f4b71Sopenharmony_ci } 566e41f4b71Sopenharmony_ci onBufferedTimeChanged(buffered: number) { 567e41f4b71Sopenharmony_ci this.handler.handleBufferedEndTimeChanged(buffered); 568e41f4b71Sopenharmony_ci } 569e41f4b71Sopenharmony_ci onTimeUpdate(time: number) { 570e41f4b71Sopenharmony_ci this.handler.handleTimeUpdate(time); 571e41f4b71Sopenharmony_ci } 572e41f4b71Sopenharmony_ci onEnded() { 573e41f4b71Sopenharmony_ci this.handler.handleEnded(); 574e41f4b71Sopenharmony_ci } 575e41f4b71Sopenharmony_ci onError() { 576e41f4b71Sopenharmony_ci this.handler.handleError(1, "Oops!"); 577e41f4b71Sopenharmony_ci } 578e41f4b71Sopenharmony_ci onVideoSizeChanged(width: number, height: number) { 579e41f4b71Sopenharmony_ci this.handler.handleVideoSizeChanged(width, height); 580e41f4b71Sopenharmony_ci this.component.onSizeChanged(width, height); 581e41f4b71Sopenharmony_ci } 582e41f4b71Sopenharmony_ci } 583e41f4b71Sopenharmony_ci 584e41f4b71Sopenharmony_ci interface Params { 585e41f4b71Sopenharmony_ci text: string 586e41f4b71Sopenharmony_ci text2: string 587e41f4b71Sopenharmony_ci web_tab: WebComponent 588e41f4b71Sopenharmony_ci handler: webview.NativeMediaPlayerHandler 589e41f4b71Sopenharmony_ci player: NativeMediaPlayerImpl 590e41f4b71Sopenharmony_ci } 591e41f4b71Sopenharmony_ci 592e41f4b71Sopenharmony_ci // Define the player components. 593e41f4b71Sopenharmony_ci @Component 594e41f4b71Sopenharmony_ci struct NativePlayerComponent { 595e41f4b71Sopenharmony_ci params?: Params 596e41f4b71Sopenharmony_ci @State bkColor: Color = Color.Red 597e41f4b71Sopenharmony_ci mXComponentController: XComponentController = new XComponentController(); 598e41f4b71Sopenharmony_ci @State player_changed: boolean = false; 599e41f4b71Sopenharmony_ci 600e41f4b71Sopenharmony_ci videoController: VideoController = new VideoController(); 601e41f4b71Sopenharmony_ci player?: AVPlayerDemo 602e41f4b71Sopenharmony_ci offset_x: number = 0 603e41f4b71Sopenharmony_ci offset_y: number = 0 604e41f4b71Sopenharmony_ci @State video_width_percent: number = 100; 605e41f4b71Sopenharmony_ci @State video_height_percent: number = 100; 606e41f4b71Sopenharmony_ci view_width: number = 0; 607e41f4b71Sopenharmony_ci view_height: number = 0; 608e41f4b71Sopenharmony_ci video_width: number = 0; 609e41f4b71Sopenharmony_ci video_height: number = 0; 610e41f4b71Sopenharmony_ci 611e41f4b71Sopenharmony_ci fullscreen: boolean = false; 612e41f4b71Sopenharmony_ci 613e41f4b71Sopenharmony_ci onSizeChanged(width: number, height: number) { 614e41f4b71Sopenharmony_ci this.video_width = width; 615e41f4b71Sopenharmony_ci this.video_height = height; 616e41f4b71Sopenharmony_ci let scale: number = this.view_width / width; 617e41f4b71Sopenharmony_ci let scaled_video_height: number = scale * height; 618e41f4b71Sopenharmony_ci this.video_height_percent = scaled_video_height / this.view_height * 100; 619e41f4b71Sopenharmony_ci } 620e41f4b71Sopenharmony_ci 621e41f4b71Sopenharmony_ci build() { 622e41f4b71Sopenharmony_ci Column() { 623e41f4b71Sopenharmony_ci Stack() { 624e41f4b71Sopenharmony_ci XComponent({ id: 'video_player_id', type: XComponentType.SURFACE, controller: this.mXComponentController }) 625e41f4b71Sopenharmony_ci .width(this.video_width_percent + '%') 626e41f4b71Sopenharmony_ci .height(this.video_height_percent + '%') 627e41f4b71Sopenharmony_ci .border({ width: 1, color: Color.Red }) 628e41f4b71Sopenharmony_ci .onLoad(()=>{ 629e41f4b71Sopenharmony_ci if (!this.params) { 630e41f4b71Sopenharmony_ci console.log('this.params is null'); 631e41f4b71Sopenharmony_ci return; 632e41f4b71Sopenharmony_ci } 633e41f4b71Sopenharmony_ci this.player = new AVPlayerDemo(); 634e41f4b71Sopenharmony_ci this.params.player?.setNativePlayer(this.player); 635e41f4b71Sopenharmony_ci this.player.setSurfaceID(this.mXComponentController.getXComponentSurfaceId()); 636e41f4b71Sopenharmony_ci this.player_changed = !this.player_changed; 637e41f4b71Sopenharmony_ci 638e41f4b71Sopenharmony_ci this.player.avPlayerLiveDemo( 639e41f4b71Sopenharmony_ci this.params.player.mediaSource, 640e41f4b71Sopenharmony_ci new AVPlayerListenerImpl(this.params.handler, this)); 641e41f4b71Sopenharmony_ci }) 642e41f4b71Sopenharmony_ci Column() { 643e41f4b71Sopenharmony_ci Row() { 644e41f4b71Sopenharmony_ci Button(this.params?.text) 645e41f4b71Sopenharmony_ci .height(50) 646e41f4b71Sopenharmony_ci .border({ width: 2, color: Color.Red }) 647e41f4b71Sopenharmony_ci .backgroundColor(this.bkColor) 648e41f4b71Sopenharmony_ci .onClick(()=>{ 649e41f4b71Sopenharmony_ci console.log('[BrowserShell] Button[' + this.params?.text + '] is clicked'); 650e41f4b71Sopenharmony_ci this.player?.play(); 651e41f4b71Sopenharmony_ci }) 652e41f4b71Sopenharmony_ci .onTouch((event: TouchEvent) => { 653e41f4b71Sopenharmony_ci event.stopPropagation(); 654e41f4b71Sopenharmony_ci }) 655e41f4b71Sopenharmony_ci Button(this.params?.text2) 656e41f4b71Sopenharmony_ci .height(50) 657e41f4b71Sopenharmony_ci .border({ width: 2, color: Color.Red }) 658e41f4b71Sopenharmony_ci .onClick(()=>{ 659e41f4b71Sopenharmony_ci console.log('[BrowserShell] Button[' + this.params?.text2 + '] is clicked'); 660e41f4b71Sopenharmony_ci this.player?.pause(); 661e41f4b71Sopenharmony_ci }) 662e41f4b71Sopenharmony_ci .onTouch((event: TouchEvent) => { 663e41f4b71Sopenharmony_ci event.stopPropagation(); 664e41f4b71Sopenharmony_ci }) 665e41f4b71Sopenharmony_ci } 666e41f4b71Sopenharmony_ci .width('100%') 667e41f4b71Sopenharmony_ci .justifyContent(FlexAlign.SpaceEvenly) 668e41f4b71Sopenharmony_ci } 669e41f4b71Sopenharmony_ci } 670e41f4b71Sopenharmony_ci } 671e41f4b71Sopenharmony_ci .width('100%') 672e41f4b71Sopenharmony_ci .height('100%') 673e41f4b71Sopenharmony_ci .onTouchIntercept((event : TouchEvent) => { 674e41f4b71Sopenharmony_ci return HitTestMode.None 675e41f4b71Sopenharmony_ci }) 676e41f4b71Sopenharmony_ci .onAreaChange((oldValue: Area, newValue: Area) => { 677e41f4b71Sopenharmony_ci this.view_width = new Number(newValue.width).valueOf() 678e41f4b71Sopenharmony_ci this.view_height = new Number(newValue.height).valueOf() 679e41f4b71Sopenharmony_ci 680e41f4b71Sopenharmony_ci this.onSizeChanged(this.video_width, this.video_height); 681e41f4b71Sopenharmony_ci }) 682e41f4b71Sopenharmony_ci } 683e41f4b71Sopenharmony_ci } 684e41f4b71Sopenharmony_ci 685e41f4b71Sopenharmony_ci @Builder 686e41f4b71Sopenharmony_ci function NativePlayerComponentBuilder(params: Params) { 687e41f4b71Sopenharmony_ci NativePlayerComponent({ params: params }) 688e41f4b71Sopenharmony_ci .backgroundColor(Color.Green) 689e41f4b71Sopenharmony_ci .border({ width: 1, color: Color.Brown }) 690e41f4b71Sopenharmony_ci .width('100%') 691e41f4b71Sopenharmony_ci .height('100%') 692e41f4b71Sopenharmony_ci } 693e41f4b71Sopenharmony_ci 694e41f4b71Sopenharmony_ci // Use NodeController to dynamically create a player component and draw the component content on the surface specified by surfaceId. 695e41f4b71Sopenharmony_ci class MyNodeController extends NodeController { 696e41f4b71Sopenharmony_ci private rootNode: BuilderNode<[Params]> | undefined; 697e41f4b71Sopenharmony_ci private isRemove = false; 698e41f4b71Sopenharmony_ci web_tab: WebComponent 699e41f4b71Sopenharmony_ci listener: webview.NativeMediaPlayerHandler 700e41f4b71Sopenharmony_ci player: NativeMediaPlayerImpl 701e41f4b71Sopenharmony_ci 702e41f4b71Sopenharmony_ci constructor(web_tab: WebComponent, surfaceId: string, listener: webview.NativeMediaPlayerHandler, player: NativeMediaPlayerImpl, renderType: NodeRenderType) { 703e41f4b71Sopenharmony_ci super() 704e41f4b71Sopenharmony_ci this.web_tab = web_tab; 705e41f4b71Sopenharmony_ci this.listener = listener; 706e41f4b71Sopenharmony_ci this.player = player; 707e41f4b71Sopenharmony_ci let uiContext = AppStorage.get<UIContext>("UIContext"); 708e41f4b71Sopenharmony_ci this.rootNode = new BuilderNode(uiContext as UIContext, { surfaceId: surfaceId, type: renderType }); 709e41f4b71Sopenharmony_ci } 710e41f4b71Sopenharmony_ci 711e41f4b71Sopenharmony_ci makeNode(uiContext: UIContext): FrameNode | null { 712e41f4b71Sopenharmony_ci if (this.rootNode) { 713e41f4b71Sopenharmony_ci return this.rootNode.getFrameNode() as FrameNode; 714e41f4b71Sopenharmony_ci } 715e41f4b71Sopenharmony_ci return null; 716e41f4b71Sopenharmony_ci } 717e41f4b71Sopenharmony_ci 718e41f4b71Sopenharmony_ci build() { 719e41f4b71Sopenharmony_ci if (this.rootNode) { 720e41f4b71Sopenharmony_ci this.rootNode.build(wrapBuilder(NativePlayerComponentBuilder), 721e41f4b71Sopenharmony_ci { "text": "play", "text2": "pause", web_tab: this.web_tab, handler: this.listener, player: this.player}) 722e41f4b71Sopenharmony_ci } 723e41f4b71Sopenharmony_ci } 724e41f4b71Sopenharmony_ci } 725e41f4b71Sopenharmony_ci 726e41f4b71Sopenharmony_ci interface PageBeginParam { 727e41f4b71Sopenharmony_ci url: string 728e41f4b71Sopenharmony_ci } 729e41f4b71Sopenharmony_ci 730e41f4b71Sopenharmony_ci @Entry 731e41f4b71Sopenharmony_ci @Component 732e41f4b71Sopenharmony_ci struct WebComponent { 733e41f4b71Sopenharmony_ci controller: WebviewController = new webview.WebviewController() 734e41f4b71Sopenharmony_ci nativePlayer? : webview.NativeMediaPlayerBridge 735e41f4b71Sopenharmony_ci page_url: Resource = $rawfile('main.html') 736e41f4b71Sopenharmony_ci node_controller?: MyNodeController 737e41f4b71Sopenharmony_ci @State show_native_media_player: boolean = false; 738e41f4b71Sopenharmony_ci @State node_width : number = 300; 739e41f4b71Sopenharmony_ci @State node_height : number = 150; 740e41f4b71Sopenharmony_ci area?: Area 741e41f4b71Sopenharmony_ci 742e41f4b71Sopenharmony_ci build() { 743e41f4b71Sopenharmony_ci Column() { 744e41f4b71Sopenharmony_ci Stack({alignContent: Alignment.TopStart}) { 745e41f4b71Sopenharmony_ci if (this.show_native_media_player) { 746e41f4b71Sopenharmony_ci NodeContainer(this.node_controller) 747e41f4b71Sopenharmony_ci .width(this.node_width + 'px') 748e41f4b71Sopenharmony_ci .height(this.node_height + 'px') 749e41f4b71Sopenharmony_ci .backgroundColor(Color.Transparent) 750e41f4b71Sopenharmony_ci .border({ width: 2, color: Color.Orange }) 751e41f4b71Sopenharmony_ci } 752e41f4b71Sopenharmony_ci Web({ src: this.page_url, controller: this.controller }) 753e41f4b71Sopenharmony_ci .enableNativeMediaPlayer({ enable: true, shouldOverlay: true }) 754e41f4b71Sopenharmony_ci .onPageBegin((event: PageBeginParam) => { 755e41f4b71Sopenharmony_ci this.controller.onCreateNativeMediaPlayer((handler: webview.NativeMediaPlayerHandler, mediaInfo: webview.MediaInfo) => { 756e41f4b71Sopenharmony_ci console.error('onCreateNativeMediaPlayer(' + JSON.stringify(mediaInfo) + ')'); 757e41f4b71Sopenharmony_ci this.nativePlayer = new NativeMediaPlayerImpl(this, handler, mediaInfo); 758e41f4b71Sopenharmony_ci return this.nativePlayer; 759e41f4b71Sopenharmony_ci }); 760e41f4b71Sopenharmony_ci }) 761e41f4b71Sopenharmony_ci .width('100%') 762e41f4b71Sopenharmony_ci .height('100%') 763e41f4b71Sopenharmony_ci .onAreaChange((oldValue: Area, newValue: Area) => { 764e41f4b71Sopenharmony_ci oldValue; 765e41f4b71Sopenharmony_ci this.area = newValue; 766e41f4b71Sopenharmony_ci }) 767e41f4b71Sopenharmony_ci } 768e41f4b71Sopenharmony_ci } 769e41f4b71Sopenharmony_ci } 770e41f4b71Sopenharmony_ci } 771e41f4b71Sopenharmony_ci ``` 772e41f4b71Sopenharmony_ci 773e41f4b71Sopenharmony_ci- Example of using video playback on the application side: 774e41f4b71Sopenharmony_ci 775e41f4b71Sopenharmony_ci ```ts 776e41f4b71Sopenharmony_ci import { media } from '@kit.MediaKit'; 777e41f4b71Sopenharmony_ci import { common } from '@kit.AbilityKit'; 778e41f4b71Sopenharmony_ci import { BusinessError } from '@kit.BasicServicesKit'; 779e41f4b71Sopenharmony_ci 780e41f4b71Sopenharmony_ci export interface AVPlayerListener { 781e41f4b71Sopenharmony_ci onPlaying() : void 782e41f4b71Sopenharmony_ci onPaused() : void 783e41f4b71Sopenharmony_ci onDurationChanged(duration: number) : void 784e41f4b71Sopenharmony_ci onBufferedTimeChanged(buffered: number) : void 785e41f4b71Sopenharmony_ci onTimeUpdate(time: number) : void 786e41f4b71Sopenharmony_ci onEnded() : void 787e41f4b71Sopenharmony_ci onError() : void 788e41f4b71Sopenharmony_ci onVideoSizeChanged(width: number, height: number): void 789e41f4b71Sopenharmony_ci } 790e41f4b71Sopenharmony_ci 791e41f4b71Sopenharmony_ci export class AVPlayerDemo { 792e41f4b71Sopenharmony_ci private surfaceID: string = ''; // The surfaceID parameter specifies the window used to display the video. Its value is obtained through the XComponent. 793e41f4b71Sopenharmony_ci private isSeek: boolean = true; // Specify whether the seek operation is supported. 794e41f4b71Sopenharmony_ci private fileSize: number = -1; 795e41f4b71Sopenharmony_ci private fd: number = 0; 796e41f4b71Sopenharmony_ci 797e41f4b71Sopenharmony_ci avPlayer?: media.AVPlayer; 798e41f4b71Sopenharmony_ci 799e41f4b71Sopenharmony_ci setSurfaceID(surface_id: string) { 800e41f4b71Sopenharmony_ci console.log('setSurfaceID : ' + surface_id) 801e41f4b71Sopenharmony_ci this.surfaceID = surface_id; 802e41f4b71Sopenharmony_ci } 803e41f4b71Sopenharmony_ci // Set AVPlayer callback functions. 804e41f4b71Sopenharmony_ci setAVPlayerCallback(avPlayer: media.AVPlayer, listener?: AVPlayerListener) { 805e41f4b71Sopenharmony_ci // Callback function for the seek operation. 806e41f4b71Sopenharmony_ci avPlayer.on('seekDone', (seekDoneTime: number) => { 807e41f4b71Sopenharmony_ci console.info(`AVPlayer seek succeeded, seek time is ${seekDoneTime}`); 808e41f4b71Sopenharmony_ci }) 809e41f4b71Sopenharmony_ci // Callback function for errors. If an error occurs during the operation on the AVPlayer, reset() is called to reset the AVPlayer. 810e41f4b71Sopenharmony_ci avPlayer.on('error', (err: BusinessError) => { 811e41f4b71Sopenharmony_ci console.error(`Invoke avPlayer failed, code is ${err.code}, message is ${err.message}`); 812e41f4b71Sopenharmony_ci listener?.onError(); 813e41f4b71Sopenharmony_ci avPlayer.reset(); // Call reset() to reset the AVPlayer, which enters the idle state. 814e41f4b71Sopenharmony_ci }) 815e41f4b71Sopenharmony_ci // Callback function for state changes. 816e41f4b71Sopenharmony_ci avPlayer.on('stateChange', async (state: string, reason: media.StateChangeReason) => { 817e41f4b71Sopenharmony_ci switch (state) { 818e41f4b71Sopenharmony_ci case 'idle': // This state is reported upon a successful callback of reset(). 819e41f4b71Sopenharmony_ci console.info('AVPlayer state idle called.'); 820e41f4b71Sopenharmony_ci avPlayer.release(); // Call release() to release the instance. 821e41f4b71Sopenharmony_ci break; 822e41f4b71Sopenharmony_ci case 'initialized': // This state is reported when the AVPlayer sets the playback source. 823e41f4b71Sopenharmony_ci console.info('AVPlayer state initialized called.'); 824e41f4b71Sopenharmony_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. 825e41f4b71Sopenharmony_ci avPlayer.prepare(); 826e41f4b71Sopenharmony_ci break; 827e41f4b71Sopenharmony_ci case 'prepared': // This state is reported upon a successful callback of prepare(). 828e41f4b71Sopenharmony_ci console.info('AVPlayer state prepared called.'); 829e41f4b71Sopenharmony_ci //avPlayer.play(); 830e41f4b71Sopenharmony_ci break; 831e41f4b71Sopenharmony_ci case 'playing': // This state is reported upon a successful callback of play(). 832e41f4b71Sopenharmony_ci console.info('AVPlayer state playing called.'); 833e41f4b71Sopenharmony_ci listener?.onPlaying(); 834e41f4b71Sopenharmony_ci break; 835e41f4b71Sopenharmony_ci case 'paused': // This state is reported upon a successful callback of pause(). 836e41f4b71Sopenharmony_ci console.info('AVPlayer state paused called.'); 837e41f4b71Sopenharmony_ci listener?.onPaused(); 838e41f4b71Sopenharmony_ci break; 839e41f4b71Sopenharmony_ci case 'completed': // This state is reported upon the completion of the playback. 840e41f4b71Sopenharmony_ci console.info('AVPlayer state completed called.'); 841e41f4b71Sopenharmony_ci avPlayer.stop(); // Call stop() to stop the playback. 842e41f4b71Sopenharmony_ci //avPlayer.seek(0); 843e41f4b71Sopenharmony_ci //avPlayer.play(); 844e41f4b71Sopenharmony_ci break; 845e41f4b71Sopenharmony_ci case 'stopped': // This state is reported upon a successful callback of stop(). 846e41f4b71Sopenharmony_ci console.info('AVPlayer state stopped called.'); 847e41f4b71Sopenharmony_ci listener?.onEnded(); 848e41f4b71Sopenharmony_ci avPlayer.reset(); // Call reset() to reset the AVPlayer. 849e41f4b71Sopenharmony_ci break; 850e41f4b71Sopenharmony_ci case 'released': 851e41f4b71Sopenharmony_ci console.info('AVPlayer state released called.'); 852e41f4b71Sopenharmony_ci break; 853e41f4b71Sopenharmony_ci default: 854e41f4b71Sopenharmony_ci console.info('AVPlayer state unknown called.'); 855e41f4b71Sopenharmony_ci break; 856e41f4b71Sopenharmony_ci } 857e41f4b71Sopenharmony_ci }) 858e41f4b71Sopenharmony_ci avPlayer.on('durationUpdate', (duration: number) => { 859e41f4b71Sopenharmony_ci console.info('AVPlayer state durationUpdate success,new duration is :' + duration) 860e41f4b71Sopenharmony_ci listener?.onDurationChanged(duration/1000); 861e41f4b71Sopenharmony_ci }) 862e41f4b71Sopenharmony_ci avPlayer.on('timeUpdate', (time:number) => { 863e41f4b71Sopenharmony_ci listener?.onTimeUpdate(time/1000); 864e41f4b71Sopenharmony_ci }) 865e41f4b71Sopenharmony_ci avPlayer.on('bufferingUpdate', (infoType: media.BufferingInfoType, value: number) => { 866e41f4b71Sopenharmony_ci console.info('AVPlayer state bufferingUpdate success,and infoType value is:' + infoType + ', value is :' + value) 867e41f4b71Sopenharmony_ci if (infoType == media.BufferingInfoType.BUFFERING_PERCENT) { 868e41f4b71Sopenharmony_ci } 869e41f4b71Sopenharmony_ci listener?.onBufferedTimeChanged(value); 870e41f4b71Sopenharmony_ci }) 871e41f4b71Sopenharmony_ci avPlayer.on('videoSizeChange', (width: number, height: number) => { 872e41f4b71Sopenharmony_ci console.info('AVPlayer state videoSizeChange success,and width is:' + width + ', height is :' + height) 873e41f4b71Sopenharmony_ci listener?.onVideoSizeChanged(width, height); 874e41f4b71Sopenharmony_ci }) 875e41f4b71Sopenharmony_ci } 876e41f4b71Sopenharmony_ci 877e41f4b71Sopenharmony_ci // The following demo shows how to play live streams by setting the network address through the URL. 878e41f4b71Sopenharmony_ci async avPlayerLiveDemo(url: string, listener?: AVPlayerListener) { 879e41f4b71Sopenharmony_ci // Create an AVPlayer instance. 880e41f4b71Sopenharmony_ci this.avPlayer = await media.createAVPlayer(); 881e41f4b71Sopenharmony_ci // Set a callback function for state changes. 882e41f4b71Sopenharmony_ci this.setAVPlayerCallback(this.avPlayer, listener); 883e41f4b71Sopenharmony_ci this.isSeek = false; // The seek operation is not supported. 884e41f4b71Sopenharmony_ci this.avPlayer.url = url; 885e41f4b71Sopenharmony_ci } 886e41f4b71Sopenharmony_ci 887e41f4b71Sopenharmony_ci play() { 888e41f4b71Sopenharmony_ci console.info('AVPlayer.play()'); 889e41f4b71Sopenharmony_ci this.avPlayer?.play() 890e41f4b71Sopenharmony_ci } 891e41f4b71Sopenharmony_ci pause() { 892e41f4b71Sopenharmony_ci console.info('AVPlayer.pause()'); 893e41f4b71Sopenharmony_ci this.avPlayer?.pause() 894e41f4b71Sopenharmony_ci } 895e41f4b71Sopenharmony_ci release() { 896e41f4b71Sopenharmony_ci console.info('AVPlayer.release()'); 897e41f4b71Sopenharmony_ci this.avPlayer?.release(); 898e41f4b71Sopenharmony_ci } 899e41f4b71Sopenharmony_ci seek(time: number) { 900e41f4b71Sopenharmony_ci console.info('AVPlayer.seek(' + time + ')'); 901e41f4b71Sopenharmony_ci this.avPlayer?.seek(time * 1000); 902e41f4b71Sopenharmony_ci } 903e41f4b71Sopenharmony_ci } 904e41f4b71Sopenharmony_ci ``` 905e41f4b71Sopenharmony_ci 906e41f4b71Sopenharmony_ci- Frontend page example. 907e41f4b71Sopenharmony_ci 908e41f4b71Sopenharmony_ci ```html 909e41f4b71Sopenharmony_ci <html> 910e41f4b71Sopenharmony_ci <head> 911e41f4b71Sopenharmony_ci <title>Video Hosting Test html</title> 912e41f4b71Sopenharmony_ci <meta name="viewport" content="width=device-width"> 913e41f4b71Sopenharmony_ci </head> 914e41f4b71Sopenharmony_ci <body> 915e41f4b71Sopenharmony_ci <div> 916e41f4b71Sopenharmony_ci <video src='https://media.w3.org/2010/05/sintel/trailer.mp4'></video> 917e41f4b71Sopenharmony_ci </div> 918e41f4b71Sopenharmony_ci </body> 919e41f4b71Sopenharmony_ci </html> 920e41f4b71Sopenharmony_ci ``` 921