diff --git a/commons/base/src/main/ets/utils/AvPlayerUtil.ets b/commons/base/src/main/ets/utils/AvPlayerUtil.ets index 8f16786299902c182ad62c39e2857a7ae86082e6..409f2a258a4ffb283f7e2364dbe7fa3a3611b020 100644 --- a/commons/base/src/main/ets/utils/AvPlayerUtil.ets +++ b/commons/base/src/main/ets/utils/AvPlayerUtil.ets @@ -24,27 +24,17 @@ export class AvPlayerUtil { private avPlayer?: media.AVPlayer; private context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; private url: resourceManager.RawFileDescriptor | null = null; - private playState: boolean = true; private surfaceId: string = ''; private sliderBegin: number = 0; - private startTime: number = 0; - private isFullScreen: boolean = false; + private isMoving: boolean = false; private onError: (err: BusinessError) => void = (err: BusinessError) => { Logger.error(`Invoke avPlayer failed, code is ${err.code}, message is ${err.message}`); - if (this.avPlayer === undefined) { - Logger.error(`AvPlayer is undefined`); - return; - } - this.avPlayer.reset(); + this.avPlayer?.reset(); } private onTimeUpdateFunction: (updateTime: number) => void = (updateTime: number) => { - if (this.avPlayer === undefined) { - Logger.error(`AvPlayer is undefined`); - return; - } AppStorage.setOrCreate(CommonConstants.AV_PLAYER_CURRENT_TIME, this.formatTime(updateTime)); AppStorage.setOrCreate(CommonConstants.AV_PLAYER_UPDATE_TIME, updateTime); - AppStorage.setOrCreate(CommonConstants.AV_PLAYER_PROGRESS, updateTime / this.avPlayer.duration * + AppStorage.setOrCreate(CommonConstants.AV_PLAYER_PROGRESS, updateTime / this.avPlayer!.duration * CommonConstants.PROGRESS_HUNDRED); } private onStateChange: (state: media.AVPlayerState) => void = async (state: media.AVPlayerState) => { @@ -75,32 +65,20 @@ export class AvPlayerUtil { case CommonConstants.AV_PLAYER_PREPARED_STATE: this.avPlayer.videoScaleType = media.VideoScaleType.VIDEO_SCALE_TYPE_FIT; Logger.info('AVPlayer state prepared called.'); - this.seekToStart(); this.avPlayer.play(); AppStorage.setOrCreate(CommonConstants.AV_PLAYER_TOTAL_TIME, this.formatTime(this.avPlayer.duration)); break; case CommonConstants.AV_PLAYER_PLAYING_STATE: Logger.info('AVPlayer state playing called.'); - this.playState = true; - if (this.isFullScreen) { - AppStorage.setOrCreate('fullScreenPlayState', this.playState); - } - this.seekToStart(); + AppStorage.setOrCreate('avplayerState', this.avPlayer.state); break; case CommonConstants.AV_PLAYER_PAUSED_STATE: Logger.info('AVPlayer state paused called.'); - this.playState = false; - if (this.isFullScreen) { - AppStorage.setOrCreate('fullScreenPlayState', this.playState); - } - this.seekToStart(); + AppStorage.setOrCreate('avplayerState', this.avPlayer.state); break; case CommonConstants.AV_PLAYER_COMPLETED_STATE: Logger.info('AVPlayer state completed called.'); - this.playState = false; - if (this.isFullScreen) { - AppStorage.setOrCreate('fullScreenPlayState', this.playState); - } + AppStorage.setOrCreate('avplayerState', this.avPlayer.state); this.avPlayer.stop(); break; case CommonConstants.AV_PLAYER_STOPPED_STATE: @@ -118,8 +96,21 @@ export class AvPlayerUtil { } } - async createAvPlayer(surfaceId: string, isFullScreen: boolean): Promise { - this.isFullScreen = isFullScreen; + static getInstance(): AvPlayerUtil | undefined { + if (!AppStorage.get('avPlayerUtil')) { + AppStorage.setOrCreate('avPlayerUtil', new AvPlayerUtil()); + } else { + Logger.info(`AppStorage does not have avPlayerUtil`); + } + return AppStorage.get('avPlayerUtil'); + } + + setSurfaceId(surfaceId: string | undefined) { + this.surfaceId = surfaceId!; + this.avPlayer!.surfaceId = surfaceId; + } + + async createAvPlayer(surfaceId: string): Promise { if (this.avPlayer === undefined || this.avPlayer.state === CommonConstants.AV_PLAYER_RELEASE_STATE) { this.avPlayer = await media.createAVPlayer(); this.surfaceId = surfaceId; @@ -134,54 +125,25 @@ export class AvPlayerUtil { } setAVPlayerCallback(): void { - if (this.avPlayer === undefined) { - Logger.error(`AvPlayer is undefined`); - return; - } - this.avPlayer.on('error', this.onError); + this.avPlayer?.on('error', this.onError); this.onTimeUpdate(); this.setStateChange(); } onTimeUpdate(): void { - if (this.avPlayer === undefined) { - Logger.error(`AvPlayer is undefined`); - return; - } - this.avPlayer.on('timeUpdate', this.onTimeUpdateFunction); + this.avPlayer?.on('timeUpdate', this.onTimeUpdateFunction); } offTimeUpdate(): void { - if (this.avPlayer === undefined) { - Logger.error(`AvPlayer is undefined`); - return; - } try { - this.avPlayer.off('timeUpdate'); + this.avPlayer?.off('timeUpdate'); } catch (exception) { Logger.error('Failed to unregister callback. Code: ' + JSON.stringify(exception)); } } setStateChange(): void { - if (this.avPlayer === undefined) { - Logger.error(`AvPlayer is undefined`); - return; - } - this.avPlayer.on('stateChange', this.onStateChange) - } - - setStartTime(startTime: number): void { - this.startTime = startTime; - } - - seekToStart(): void { - if (this.startTime != 0 && this.avPlayer !== undefined) { - this.avPlayer.seek(this.startTime, media.SeekMode.SEEK_PREV_SYNC); - this.startTime = 0; - } else { - Logger.info(`Video is played from the beginning`); - } + this.avPlayer?.on('stateChange', this.onStateChange); } release(): void { @@ -201,27 +163,27 @@ export class AvPlayerUtil { sliderChange(value: number, mode: SliderChangeMode): void { let seekType: media.SeekMode = value > this.sliderBegin ? media.SeekMode.SEEK_PREV_SYNC : media.SeekMode.SEEK_NEXT_SYNC; - if (this.avPlayer === undefined) { - Logger.error(`AvPlayer is undefined`); - return; - } switch (mode) { case SliderChangeMode.Begin: Logger.info(`AvPlayer SliderChangeMode Begin`); this.sliderBegin = value; - this.avPlayer.pause(); + this.avPlayer?.pause(); break; case SliderChangeMode.Moving: Logger.info(`AvPlayer SliderChangeMode Moving`); - this.avPlayer.seek(value / CommonConstants.PROGRESS_HUNDRED * this.avPlayer.duration, seekType); + this.isMoving = true; break; case SliderChangeMode.End: Logger.info(`AvPlayer SliderChangeMode End`); - this.avPlayer.play(); + if (this.isMoving) { + this.avPlayer?.seek(value / CommonConstants.PROGRESS_HUNDRED * this.avPlayer.duration, seekType); + this.isMoving = false; + } + this.avPlayer?.play(); break; case SliderChangeMode.Click: Logger.info(`AvPlayer SliderChangeMode Click`); - this.avPlayer.seek(this.sliderBegin / CommonConstants.PROGRESS_HUNDRED * this.avPlayer.duration, seekType); + this.avPlayer?.seek(this.sliderBegin / CommonConstants.PROGRESS_HUNDRED * this.avPlayer.duration, seekType); break; default: break; @@ -229,37 +191,15 @@ export class AvPlayerUtil { } playerStateControl(): void { - if (this.avPlayer === undefined) { - Logger.info(`AvPlayer is undefined`); - return; - } - if (this.avPlayer.state === CommonConstants.AV_PLAYER_STOPPED_STATE) { + if (this.avPlayer?.state === CommonConstants.AV_PLAYER_STOPPED_STATE) { this.avPlayer.prepare(); - return; - } - if (!this.playState) { + } else if (this.avPlayer?.state === CommonConstants.AV_PLAYER_PAUSED_STATE) { this.avPlayer.play(); - } else { + } else if (this.avPlayer?.state === CommonConstants.AV_PLAYER_PLAYING_STATE) { this.avPlayer.pause(); } } - play(): void { - if (this.avPlayer !== undefined && !this.playState) { - this.avPlayer.play(); - } else { - Logger.info(`AvPlayer play failed`); - } - } - - pause(): void { - if (this.avPlayer !== undefined && this.playState) { - this.avPlayer.pause(); - } else { - Logger.info(`AvPlayer pause failed`); - } - } - formatTime(duration: number): string { let totalSecond: number = Math.round(duration / CommonConstants.PROGRESS_THOUSAND); let hourNum: number = Math.floor(totalSecond / CommonConstants.SECOND_IN_HOUR); diff --git a/features/videoDetail/src/main/ets/view/VideoDetail.ets b/features/videoDetail/src/main/ets/view/VideoDetail.ets index 983ded766bd6a4824171f6fa1751f6b6db0715b6..d6e9e0ea2c64d2696113b5b22760505dfa54fe0a 100644 --- a/features/videoDetail/src/main/ets/view/VideoDetail.ets +++ b/features/videoDetail/src/main/ets/view/VideoDetail.ets @@ -25,15 +25,15 @@ import { DetailConstants } from '../constants/DetailConstants'; export struct VideoDetail { @StorageLink('currentWidthBreakpoint') currentWidthBreakpoint: string = BreakpointConstants.BREAKPOINT_LG; @StorageLink('windowWidth') windowWidth: number = 0; - @StorageLink('updateTime') updateTime: number = 0; @StorageLink('isHalfFolded') isHalfFolded: boolean = false; + @StorageLink('avplayerState') avplayerState: string = ''; @Consume('pageInfo') pageInfo: NavPathStack; @State commentImgHeight: string = DetailConstants.INITIAL_COMMENT_IMAGE_HEIGHT; @State commentImgWidth: string = DetailConstants.INITIAL_COMMENT_IMAGE_WIDTH; @State relatedVideoHeight: number = DetailConstants.INITIAL_RELATED_VIDEO_HEIGHT; @State videoHeight: number = DetailConstants.INITIAL_VIDEO_HEIGHT; @State screenWidth: number = DeviceScreen.getDeviceWidth(); - private avPlayerUtil: AvPlayerUtil= new AvPlayerUtil(); + private avPlayerUtil?: AvPlayerUtil; private screenHeight: number = 0; private windowUtil?: WindowUtil; private onDetailFoldStatusChange: Callback = (data: display.FoldStatus) => { @@ -62,6 +62,7 @@ export struct VideoDetail { }; aboutToAppear() { + this.avPlayerUtil = AvPlayerUtil.getInstance(); DisplayUtil.getFoldCreaseRegion(); this.screenHeight = DeviceScreen.getDeviceHeight(); this.windowUtil = WindowUtil.getInstance(); @@ -74,15 +75,15 @@ export struct VideoDetail { } } - aboutToDisappear() { - this.updateTime = 0; + async aboutToDisappear() { + this.avPlayerUtil?.offTimeUpdate(); + await this.avPlayerUtil?.release(); if (this.windowUtil === undefined) { return; } if (this.currentWidthBreakpoint === BreakpointConstants.BREAKPOINT_MD && display.isFoldable()) { this.windowUtil.setMainWindowOrientation(window.Orientation.PORTRAIT); } - this.avPlayerUtil.release(); } build() { @@ -140,9 +141,7 @@ export struct VideoDetail { Column() { VideoDetailView({ screenHeight: this.screenHeight, - avPlayerUtil: this.avPlayerUtil, - relatedVideoHeight: - $relatedVideoHeight, + relatedVideoHeight: $relatedVideoHeight, videoHeight: $videoHeight }) .layoutWeight(1) @@ -183,15 +182,15 @@ export struct VideoDetail { } catch (exception) { Logger.error('Failed to register callback. Code: ' + JSON.stringify(exception)); } + if (this.avplayerState !== CommonConstants.AV_PLAYER_PLAYING_STATE) { + this.avPlayerUtil?.playerStateControl(); + } if (this.windowUtil === undefined) { return; } if (this.currentWidthBreakpoint === BreakpointConstants.BREAKPOINT_MD && display.isFoldable()) { this.isHalfFolded = false; } - this.avPlayerUtil.setStartTime(this.updateTime); - this.avPlayerUtil.playerStateControl(); - this.avPlayerUtil.onTimeUpdate(); }) .onHidden(() => { try { @@ -199,8 +198,6 @@ export struct VideoDetail { } catch (exception) { Logger.error('Failed to unregister callback. Code: ' + JSON.stringify(exception)); } - this.avPlayerUtil.pause(); - this.avPlayerUtil.offTimeUpdate(); }) } } \ No newline at end of file diff --git a/features/videoDetail/src/main/ets/view/VideoDetailView.ets b/features/videoDetail/src/main/ets/view/VideoDetailView.ets index 5172d7a29d8e0d49e112f3005a01500e91bdedbf..fc24a9ab12d02e02bd58f1145e75971a76c03afa 100644 --- a/features/videoDetail/src/main/ets/view/VideoDetailView.ets +++ b/features/videoDetail/src/main/ets/view/VideoDetailView.ets @@ -29,11 +29,14 @@ export struct VideoDetailView { @Consume('pageInfo') pageInfo: NavPathStack; @Link relatedVideoHeight: number; @Link videoHeight: number; - private avPlayerUtil: AvPlayerUtil = new AvPlayerUtil(); + private avPlayerUtil?: AvPlayerUtil; private screenHeight: number = 0; - private surfaceId: string = ''; - private xComponentController: XComponentController = new XComponentController(); private scroller: Scroller = new Scroller(); + private xComponentController: XComponentController = new XComponentController(); + + aboutToAppear(): void { + this.avPlayerUtil = AvPlayerUtil.getInstance(); + } build() { Scroll(this.scroller) { @@ -45,8 +48,8 @@ export struct VideoDetailView { controller: this.xComponentController }) .onLoad(() => { - this.surfaceId = this.xComponentController.getXComponentSurfaceId(); - this.avPlayerUtil.createAvPlayer(this.surfaceId, false); + this.avPlayerUtil?.createAvPlayer(this.xComponentController.getXComponentSurfaceId()); + AppStorage.setOrCreate('detailSurfaceId', this.xComponentController.getXComponentSurfaceId()); }) .width(this.videoHeight + DetailConstants.PERCENT_SIGN) .height(CommonConstants.FULL_PERCENT) @@ -66,7 +69,7 @@ export struct VideoDetailView { value: this.progress }) .onChange((value: number, mode: SliderChangeMode) => { - this.avPlayerUtil.sliderChange(value, mode); + this.avPlayerUtil?.sliderChange(value, mode); }) .layoutWeight(1) .selectedColor($r('app.color.episodes_font')) @@ -106,7 +109,7 @@ export struct VideoDetailView { .width(CommonConstants.FULL_PERCENT) .backgroundColor(Color.Black) .onClick(() => { - this.avPlayerUtil.playerStateControl(); + this.avPlayerUtil?.playerStateControl(); }) RelatedList({ diff --git a/features/videoPlayer/src/main/ets/view/VideoPlayer.ets b/features/videoPlayer/src/main/ets/view/VideoPlayer.ets index 9b48022e9ff8bf32bd5a5c81daaca54c2055da4a..dd473cfcf547c6da2e53a259b3c068616fdbcda0 100644 --- a/features/videoPlayer/src/main/ets/view/VideoPlayer.ets +++ b/features/videoPlayer/src/main/ets/view/VideoPlayer.ets @@ -26,19 +26,17 @@ export struct VideoPlayer { @StorageLink('currentWidthBreakpoint') currentWidthBreakpoint: string = BreakpointConstants.BREAKPOINT_LG; @StorageLink('currentHeightBreakpoint') currentHeightBreakpoint: string = BreakpointConstants.BREAKPOINT_LG; @StorageLink('creaseRegion') creaseRegion: number[] = []; - @StorageLink('updateTime') updateTime: number = 0; @StorageLink('currentTime') currentTime: string = CommonConstants.INITIAL_TIME; @StorageLink('totalTime') totalTime: string = CommonConstants.INITIAL_TIME; @StorageLink('progress') progress: number = 0; - @StorageLink('fullScreenPlayState') fullScreenPlayState: boolean = true; + @StorageLink('avplayerState') avplayerState: string = ''; @StorageLink('isHalfFolded') isHalfFolded: boolean = false; @State isShowingSideBar: boolean = false; @State foldStatus: display.FoldStatus = display.getFoldStatus(); @Consume('pageInfo') pageInfo: NavPathStack; private windowUtil?: WindowUtil; - private avPlayerUtil: AvPlayerUtil = new AvPlayerUtil(); + private avPlayerUtil?: AvPlayerUtil; private xComponentController: XComponentController = new XComponentController(); - private surfaceId: string = ''; private onFoldStatusChange: Callback = (data: display.FoldStatus) => { this.foldStatus = data; if (data === display.FoldStatus.FOLD_STATUS_EXPANDED) { @@ -61,6 +59,7 @@ export struct VideoPlayer { }; aboutToAppear() { + this.avPlayerUtil = AvPlayerUtil.getInstance(); this.windowUtil = WindowUtil.getInstance(); if (this.windowUtil !== undefined) { /** @@ -87,11 +86,10 @@ export struct VideoPlayer { } else { Logger.info(`Full-screen display in portrait mode`); } - this.avPlayerUtil.setStartTime(this.updateTime); } - aboutToDisappear() { - this.avPlayerUtil.release(); + async aboutToDisappear() { + this.avPlayerUtil?.setSurfaceId(AppStorage.get('detailSurfaceId')); if (this.windowUtil !== undefined) { /** * When step out the fullScreen VideoPlay, any device need to show systemBar except 2ini. @@ -128,8 +126,7 @@ export struct VideoPlayer { controller: this.xComponentController }) .onLoad(() => { - this.surfaceId = this.xComponentController.getXComponentSurfaceId(); - this.avPlayerUtil.createAvPlayer(this.surfaceId, true); + this.avPlayerUtil?.setSurfaceId(this.xComponentController.getXComponentSurfaceId()); }) .aspectRatio(CommonConstants.VIDEO_ASPECT_RATIO) } @@ -155,7 +152,7 @@ export struct VideoPlayer { Slider({ min: 0, max: CommonConstants.PROGRESS_HUNDRED, step: 1, value: this.progress }) .onChange((value: number, mode: SliderChangeMode) => { - this.avPlayerUtil.sliderChange(value, mode); + this.avPlayerUtil?.sliderChange(value, mode); }) .layoutWeight(1) .selectedColor($r('app.color.selected_color')) @@ -172,12 +169,13 @@ export struct VideoPlayer { Row() { Row() { - Image(this.fullScreenPlayState ? $r('app.media.ic_public_pause') : $r('app.media.ic_public_play')) + Image(this.avplayerState === CommonConstants.AV_PLAYER_PLAYING_STATE ? $r('app.media.ic_public_pause') + : $r('app.media.ic_public_play')) .height($r('app.float.icon_size')) .width($r('app.float.icon_size')) .margin({ left: $r('app.float.icon_margin') }) .onClick(() => { - this.avPlayerUtil.playerStateControl(); + this.avPlayerUtil?.playerStateControl(); }) ImgIcon({ img: $r('app.media.ic_public_play_next') }) ImgIcon({ img: $r('app.media.ic_public_view_list_white') }) @@ -185,7 +183,7 @@ export struct VideoPlayer { .margin({ top: this.currentWidthBreakpoint === BreakpointConstants.BREAKPOINT_SM ? '0' : $r('app.float.icon_row_top'), bottom: this.currentWidthBreakpoint === BreakpointConstants.BREAKPOINT_SM ? - $r('app.float.icon_row_bottom_sm') : $r('app.float.icon_row_bottom') + $r('app.float.icon_row_bottom_sm') : $r('app.float.icon_row_bottom') }) Blank() @@ -248,15 +246,16 @@ export struct VideoPlayer { } .hideTitleBar(true) .onShown(() => { + if (this.avplayerState !== CommonConstants.AV_PLAYER_PLAYING_STATE) { + this.avPlayerUtil?.playerStateControl(); + } try { display.on('foldStatusChange', this.onFoldStatusChange); } catch (exception) { Logger.error('Failed to register callback. Code: ' + JSON.stringify(exception)); } - this.avPlayerUtil.onTimeUpdate(); }) .onHidden(() => { - this.avPlayerUtil.offTimeUpdate(); display.off('foldStatusChange'); }) }