diff --git a/windowpiplibrary/src/main/ets/component/VideoPlayComponent.ets b/windowpiplibrary/src/main/ets/component/VideoPlayComponent.ets index 3d1b215689165ac8072e0c38ac57cf5d37bb6074..23f18eebbb0dec3c1d7c323a456c4e5f2d79e5b7 100644 --- a/windowpiplibrary/src/main/ets/component/VideoPlayComponent.ets +++ b/windowpiplibrary/src/main/ets/component/VideoPlayComponent.ets @@ -14,6 +14,7 @@ */ import { PiPWindow } from '@kit.ArkUI'; +import { BusinessError } from '@kit.BasicServicesKit'; import { AVPlayer } from '../player/AVPlayer'; import Logger from '../utils/Logger'; import { CommonConstants } from '../constants/CommonConstants'; @@ -68,29 +69,20 @@ export struct VideoPlayComponent { @State buttonAction: string = ''; @State isAutoPull: boolean = false; @State hintMsgVisibility: boolean = false; + @State navigationId: string = ''; private mXComponentController = new XComponentController(); private surfaceId = ''; - @State navigationId: string = ''; private player?: AVPlayer; private pipController: PiPWindow.PiPController | undefined = undefined; private eventHub = this.getUIContext().getHostContext()!.eventHub; private scrollerForScroll: Scroller = new Scroller(); - aboutToAppear(): void { - this.eventHub.on('onStateChange', (fg: boolean) => { - if (fg && this.curState === 'STARTED') { - this.stopPip(); - } - }); - } - aboutToDisappear(): void { this.destroyPipController(); if (this.player) { this.player.stopAvPlayer(); this.player = undefined; } - this.eventHub?.off('onStateChange'); } changePipControllerStatusStart(): void { @@ -106,8 +98,13 @@ export struct VideoPlayComponent { Logger.info(TAG, `pipController create error`); return; } - await this.pipController.startPiP(); - this.changePipControllerStatusStart(); + try { + await this.pipController.startPiP(); + this.changePipControllerStatusStart(); + } catch (error) { + const err: BusinessError = error as BusinessError; + Logger.error(TAG, `Invoke pipController failed, code is ${err.code}, message is ${err.message}`); + } } async stopPip(): Promise { @@ -115,17 +112,27 @@ export struct VideoPlayComponent { Logger.info(TAG, `pipController is not exist`); return; } - await this.pipController.stopPiP(); + try { + await this.pipController.stopPiP(); + } catch (error) { + const err: BusinessError = error as BusinessError; + Logger.error(TAG, `Invoke pipController failed, code is ${err.code}, message is ${err.message}`); + } } async createPip(): Promise { - if (this.pipController) { - this.pipController.off('stateChange'); - this.pipController.off('controlPanelActionEvent'); - await this.pipController.stopPiP(); + try { + if (this.pipController) { + this.pipController.off('stateChange'); + this.pipController.off('controlPanelActionEvent'); + await this.pipController.stopPiP(); + } + await this.createPipController(); + await this.startPip(); + } catch (error) { + const err: BusinessError = error as BusinessError; + Logger.error(TAG, `Invoke pipController failed, code is ${err.code}, message is ${err.message}`); } - await this.createPipController(); - await this.startPip(); } async createPipController(): Promise { @@ -219,7 +226,7 @@ export struct VideoPlayComponent { showToast() { try { - this.getUIContext().getPromptAction().showToast(({ + this.getUIContext().getPromptAction().showToast(({ message: $r('app.string.notsupport'), duration: 2000 })); @@ -239,20 +246,23 @@ export struct VideoPlayComponent { .height($r('app.integer.auto_button_height')) .selectedColor($r('sys.color.brand')) .onChange(async (isOn: boolean) => { - if (isOn) { - if (this.curBp === 'xl') { - this.showToast(); - return; + try { + if (isOn) { + if (this.curBp === 'xl') { + this.showToast(); + return; + } } + this.isAutoPull = isOn; + if (!this.pipController) { + await this.createPipController(); + } + this.pipController?.setAutoStartEnabled(this.isAutoPull); + this.hintMsgVisibility = true; + } catch (error) { + const err: BusinessError = error as BusinessError; + Logger.error(TAG, `Invoke pipController failed, code is ${err.code}, message is ${err.message}`); } - - this.isAutoPull = isOn; - if (!this.pipController) { - await this.createPipController(); - } - this.pipController?.setAutoStartEnabled(this.isAutoPull); - this.hintMsgVisibility = true; - }) } .width(CommonConstants.FULL_PERCENT) @@ -359,7 +369,8 @@ export struct VideoPlayComponent { .onLoad(() => { Logger.info(TAG, ` XComponent onLoad`); this.surfaceId = this.mXComponentController.getXComponentSurfaceId(); - this.player = new AVPlayer(this.surfaceId, CommonConstants.AVPLAYER_TYPE, this.getUIContext().getHostContext()!); + this.player = + new AVPlayer(this.surfaceId, CommonConstants.AVPLAYER_TYPE, this.getUIContext().getHostContext()!); this.player.avPlayerFdSrc(); }) .onDestroy(() => { @@ -376,7 +387,7 @@ export struct VideoPlayComponent { Row() { SymbolGlyph(this.curState === 'STOPPED' ? $r('sys.symbol.enlarge_window') : - $r('sys.symbol.smal_window_playback')) + $r('sys.symbol.smal_window_playback')) .fontColor([$r('sys.color.icon_on_primary')]) .fontSize($r('sys.float.Title_M')) } diff --git a/windowpiplibrary/src/main/ets/player/AVPlayer.ets b/windowpiplibrary/src/main/ets/player/AVPlayer.ets index 2310a3c92afe9c8094f967c78b3289ec033688b4..ac7665b1e3d61ac60e9a5d8f2cf28c0f9bbd49b5 100644 --- a/windowpiplibrary/src/main/ets/player/AVPlayer.ets +++ b/windowpiplibrary/src/main/ets/player/AVPlayer.ets @@ -22,12 +22,12 @@ import Logger from '../utils/Logger'; const TAG: string = '[AVPlayer]'; export class AVPlayer { + // It is used to distinguish between the player of the main interface and the player of the pip interface. + public type: number = 0; private avPlayer?: media.AVPlayer; // The surfaceID is used to display the screen. private surfaceID: string; private jumpNext: boolean = false; - // It is used to distinguish between the player of the main interface and the player of the pip interface. - public type: number = 0; private state: string = ''; private playStatus: boolean = true; private context: Context; @@ -36,17 +36,6 @@ export class AVPlayer { this.surfaceID = surfaceID; this.type = type; this.context = context; - this.context.eventHub.on('appStateChange', (fg: boolean) => { - if (fg) { - if (this.state === 'paused') { - this.avPlayer?.play(); - } - } else { - if (this.state === 'playing') { - this.avPlayer?.pause(); - } - } - }); } updatePlayStatus(status: boolean): void { @@ -61,7 +50,7 @@ export class AVPlayer { }); // error callback listens to the function. this.avPlayer?.on('error', (err: BusinessError) => { - Logger.info(TAG, `Invoke avPlayer failed, code is ${err.code}, message is ${err.message}`); + Logger.error(TAG, `Invoke avPlayer failed, code is ${err.code}, message is ${err.message}`); // Call the RESET call to reset the resource to trigger the idle state. this.avPlayer?.reset(); this.avPlayer?.play(); @@ -72,66 +61,72 @@ export class AVPlayer { return; } this.state = state; - switch (state) { - // After the RESET API is successfully called, the state machine is triggered. - case 'idle': - Logger.info(TAG, 'AVPlayer state idle called.'); - if (!this.jumpNext) { - // Call the release operation to destroy the instance object. - this.avPlayer?.release(); - } else { - const context = this.context as common.UIAbilityContext; - const fileDescriptor: resourceManager.RawFileDescriptor = await context.resourceManager.getRawFd('video.mp4'); - this.avPlayer.fdSrc = fileDescriptor; - } - break; + try { + switch (state) { + // After the RESET API is successfully called, the state machine is triggered. + case 'idle': + Logger.info(TAG, 'AVPlayer state idle called.'); + if (!this.jumpNext) { + // Call the release operation to destroy the instance object. + await this.avPlayer?.release(); + } else { + const context = this.context as common.UIAbilityContext; + const fileDescriptor: resourceManager.RawFileDescriptor = + await context.resourceManager.getRawFd('video.mp4'); + this.avPlayer.fdSrc = fileDescriptor; + } + break; // After avplayer sets the playback source, this status is reported. - case 'initialized': - Logger.info(TAG, 'AVPlayer state initialized called.'); - // You don't need to set the display screen when the playback asset is audio-only. - this.avPlayer.surfaceId = this.surfaceID; - this.avPlayer.prepare().then(() => { - Logger.info(TAG, 'AVPlayer prepare succeeded.'); - }, (err: BusinessError) => { - Logger.error(TAG, `Invoke prepare failed, code is ${err.code}, message is ${err.message}`); - }); - break; + case 'initialized': + Logger.info(TAG, 'AVPlayer state initialized called.'); + // You don't need to set the display screen when the playback asset is audio-only. + this.avPlayer.surfaceId = this.surfaceID; + this.avPlayer.prepare().then(() => { + Logger.info(TAG, 'AVPlayer prepare succeeded.'); + }, (err: BusinessError) => { + Logger.error(TAG, `Invoke prepare failed, code is ${err.code}, message is ${err.message}`); + }); + break; // After the prepare call is successful, the state machine is reported. - case 'prepared': - Logger.info(TAG, 'AVPlayer state prepared called.'); - // Call the playback API to start playback. - if (this.playStatus) { - this.avPlayer.play(); - } - break; + case 'prepared': + Logger.info(TAG, 'AVPlayer state prepared called.'); + // Call the playback API to start playback. + if (this.playStatus) { + await this.avPlayer.play(); + } + break; // After the play is successfully invoked, the state machine is triggered. - case 'playing': - Logger.info(TAG, 'AVPlayer state playing called.'); - this.jumpNext = false; - break; + case 'playing': + Logger.info(TAG, 'AVPlayer state playing called.'); + this.jumpNext = false; + break; // After pause is successfully invoked, the state machine is triggered. - case 'paused': - Logger.info(TAG, 'AVPlayer state paused called.'); - break; + case 'paused': + Logger.info(TAG, 'AVPlayer state paused called.'); + break; // After the playback ends, the state machine is triggered. - case 'completed': - Logger.info(TAG, 'AVPlayer state completed called.'); - // Call the playback end API. - this.playNext(); - break; + case 'completed': + Logger.info(TAG, 'AVPlayer state completed called.'); + // Call the playback end API. + this.playNext(); + break; // After the stop API is successfully called, the state machine is triggered. - case 'stopped': - Logger.info(TAG, 'AVPlayer state stopped called.'); - // Call the reset operation to initialize the avplayer state. - this.avPlayer.reset(); - break; - case 'released': - Logger.info(TAG, 'AVPlayer state released called.'); - this.avPlayer.release(); - break; - default: - Logger.info(TAG, 'AVPlayer state unknown called.'); - break; + case 'stopped': + Logger.info(TAG, 'AVPlayer state stopped called.'); + // Call the reset operation to initialize the avplayer state. + await this.avPlayer.reset(); + break; + case 'released': + Logger.info(TAG, 'AVPlayer state released called.'); + await this.avPlayer.release(); + break; + default: + Logger.info(TAG, 'AVPlayer state unknown called.'); + break; + } + } catch (error) { + const err: BusinessError = error as BusinessError; + Logger.error(TAG, `Invoke avPlayer failed, code is ${err.code}, message is ${err.message}`); } }); this.avPlayer?.on('videoSizeChange', (width: number, height: number) => { @@ -144,14 +139,19 @@ export class AVPlayer { * and use the fdSrc attribute to play back. */ async avPlayerFdSrc(): Promise { - // Create an avPlayer instance object. - this.avPlayer = await media.createAVPlayer(); - // Create a callback function for state machine changes. - this.setAVPlayerCallback(); - const context = this.context as common.UIAbilityContext; - const fileDescriptor = await context.resourceManager.getRawFd('video.mp4'); - // Assign a value to fdSrc to trigger an initialized state machine report. - this.avPlayer.fdSrc = fileDescriptor; + try { + // Create an avPlayer instance object. + this.avPlayer = await media.createAVPlayer(); + // Create a callback function for state machine changes. + this.setAVPlayerCallback(); + const context = this.context as common.UIAbilityContext; + const fileDescriptor = await context.resourceManager.getRawFd('video.mp4'); + // Assign a value to fdSrc to trigger an initialized state machine report. + this.avPlayer.fdSrc = fileDescriptor; + } catch (error) { + const err: BusinessError = error as BusinessError; + Logger.error(TAG, `create avplayer or getRawFd failed, code is ${err.code}, message is ${err.message}`); + } } async playNext(): Promise { @@ -159,19 +159,28 @@ export class AVPlayer { return; } this.jumpNext = true; - this.avPlayer?.stop(); + try { + await this.avPlayer?.stop(); + } catch (error) { + const err: BusinessError = error as BusinessError; + Logger.error(TAG, `Invoke avPlayer failed, code is ${err.code}, message is ${err.message}`); + } } play(): void { if ((this.state === 'paused' || this.state === 'prepared') && this.playStatus === true) { - this.avPlayer?.play(); + this.avPlayer?.play().catch((err: BusinessError) => { + Logger.error(TAG, `Invoke avPlayer failed, code is ${err.code}, message is ${err.message}`); + }); } } pause(): void { if (this.state === 'playing') { - this.avPlayer?.pause(); + this.avPlayer?.pause().catch((err: BusinessError) => { + Logger.error(TAG, `Invoke avPlayer failed, code is ${err.code}, message is ${err.message}`); + }); } } @@ -179,7 +188,9 @@ export class AVPlayer { if (!this.avPlayer) { return; } - this.avPlayer.stop(); - this.avPlayer.reset(); + Promise.all([this.avPlayer.stop(), this.avPlayer.reset()]) + .catch((err: BusinessError) => { + Logger.error(TAG, `Invoke avPlayer failed, code is ${err.code}, message is ${err.message}`); + }); } } \ No newline at end of file