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