liyubing

feat: 1)处理直播预告视频播放;2)处理视频资源播放完毕状态,支持重新播放

... ... @@ -11,7 +11,7 @@ import { UserInfoDTO } from './UserInfoDTO'
* 接口定义:
* http://192.168.1.3:3300/project/3802/interface/api/200915
*/
export interface ContentDetailDTO {
export class ContentDetailDTO {
newsId: number;
newsTitle: string;
newsShortTitle: string;
... ... @@ -72,4 +72,8 @@ export interface ContentDetailDTO {
isNewspaper: boolean;
oldNewsId: string;
serials: any;
// 本地字段
showTime:boolean = false;
}
\ No newline at end of file
... ...
... ... @@ -112,6 +112,23 @@ export struct DetailPlayLiveCommon {
})
}
// /**
// *
// * @returns true : 沉浸式;false : 非沉浸式
// */
// isImmersionLive(): boolean {
//
// let flag = false
//
// if (this.liveState === 'wait' || this.liveLandscape === 'news') {
// flag = false
// } else if (this.liveLandscape === 'general') {
// flag = true
// }
//
// return flag
// }
onPageShow() {
this.pageShow = Math.random()
Logger.info(TAG, 'onPageShow')
... ...
... ... @@ -119,8 +119,8 @@ export struct DetailPlayLivePage {
getLiveDetails() {
const data = this.contentDetailData
console.error("XXXXZZZZ", 'contentDetailData ----liveState==>' + data.liveInfo?.liveState)
console.error("XXXXZZZZ", 'contentDetailData ----liveStyle==>' + data.liveInfo?.liveStyle)
// console.error("XXXXZZZZ", 'contentDetailData ----liveState==>' + data.liveInfo?.liveState)
// console.error("XXXXZZZZ", 'contentDetailData ----liveStyle==>' + data.liveInfo?.liveStyle)
if (data.liveInfo?.liveState == 'wait') {
//直播样式 0-正常模式 , 1-隐藏直播间,2-隐藏大家聊 【人民号发布是竖屏的,为空】
if (data.liveInfo?.liveStyle == 1) {
... ...
import { window } from '@kit.ArkUI'
import { window } from '@kit.ArkUI';
import lottie from '@ohos/lottie';
import { NumberFormatterUtils, StringUtils, WindowModel } from 'wdKit/Index'
import { DateFormatUtil, WDAliPlayerController, WDPlayerController } from 'wdPlayer/Index'
import { ContentDetailDTO, LiveDetailsBean, LiveRoomDataBean } from 'wdBean/Index'
import { DisplayDirection } from 'wdConstant/Index'
import { LiveFollowComponent, LottieView } from 'wdComponent/Index'
import { NumberFormatterUtils, StringUtils, WindowModel } from 'wdKit/Index';
import { DateFormatUtil, PlayerConstants, WDAliPlayerController } from 'wdPlayer/Index';
import { ContentDetailDTO, LiveRoomDataBean } from 'wdBean/Index';
import { DisplayDirection } from 'wdConstant/Index';
import { LiveFollowComponent, LottieView } from 'wdComponent/Index';
@Component
export struct PlayUIComponent {
playerController?: WDAliPlayerController;
//菜单键是否可见
@State @Watch('onChangeMenuVisible') isMenuVisible: boolean = true
// @Consume liveDetailsBean: LiveDetailsBean
@Consume contentDetailData: ContentDetailDTO
@Consume liveRoomDataBean: LiveRoomDataBean
@State currentTime: string = ''
... ... @@ -21,8 +20,11 @@ export struct PlayUIComponent {
//是否处于播放状态中
@State isPlayStatus: boolean = true
@Consume displayDirection: DisplayDirection
@Prop isShowBottom: boolean
// 播放地址 (视频或者 直播地址)
@Prop liveUrl: string
// 当前播放资源的状态
@Consume playSourceState: number
onChangeMenuVisible() {
if (!this.contentDetailData || !this.contentDetailData.liveInfo ||
... ... @@ -41,6 +43,21 @@ export struct PlayUIComponent {
aboutToAppear(): void {
this.onChangeMenuVisible()
this.initPlayerSet()
}
aboutToDisappear(): void {
if (this.contentDetailData.liveInfo?.liveState == 'running') {
lottie.destroy('live_status_wait')
}
}
/*
初始话播放器设置
*/
initPlayerSet(){
//播放进度监听
if (this.playerController) {
this.playerController.onTimeUpdate = (position: number, duration: number) => {
... ... @@ -49,12 +66,7 @@ export struct PlayUIComponent {
this.progressVal = Math.floor(position * 100 / duration);
}
}
}
aboutToDisappear(): void {
if (this.contentDetailData.liveInfo?.liveState == 'running') {
lottie.destroy('live_status_wait')
}
}
build() {
... ... @@ -62,7 +74,7 @@ export struct PlayUIComponent {
if (this.contentDetailData && this.contentDetailData.liveInfo) {
this.getTopUIComponent()
this.getMiddleUIComponent()
if(this.isShowBottom){
if (this.isShowBottom) {
this.getBottomUIComponent()
}
... ... @@ -251,16 +263,13 @@ export struct PlayUIComponent {
@Builder
getBottomUIComponent() {
Row() {
if (this.contentDetailData?.liveInfo?.liveState == 'wait') {
Blank()
} else if (this.contentDetailData?.liveInfo?.liveState == 'running') {
// 视频资源
if (!StringUtils.isEmpty(this.liveUrl)) {
// 暂定和播放按钮
this.playOrPauseBtn()
Blank()
} else if (this.contentDetailData?.liveInfo?.liveState == 'end') {
if (StringUtils.isEmpty(this.contentDetailData?.liveInfo?.vlive[0]?.replayUri)) {
Blank()
} else {
this.playOrPauseBtn()
// 开始时间
if (this.contentDetailData?.liveInfo?.liveState != 'running' || this.contentDetailData.showTime) {
Text(this.currentTime)
.fontColor(Color.White)
.fontWeight(600)
... ... @@ -268,7 +277,12 @@ export struct PlayUIComponent {
.margin({
left: 16
})
this.playProgressView()
}
// 进度条
this.playProgressView()
// 总的播放时间
if (this.contentDetailData?.liveInfo?.liveState != 'running' || this.contentDetailData.showTime) {
Text(this.totalTime)
.fontColor(Color.White)
.fontWeight(600)
... ... @@ -277,11 +291,13 @@ export struct PlayUIComponent {
right: 16
})
}
} else {
Blank()
}
if (this.contentDetailData?.liveInfo?.liveState == 'running'
|| (this.contentDetailData?.liveInfo?.liveState == 'end' &&
StringUtils.isNotEmpty(this.contentDetailData?.liveInfo?.vlive[0]?.replayUri))
) {
// 全屏按钮
if (!StringUtils.isEmpty(this.liveUrl)) {
Image($r('app.media.icon_live_player_full_screen'))
.width(24)
.height(24)
... ... @@ -298,6 +314,7 @@ export struct PlayUIComponent {
})
.visibility(this.displayDirection == DisplayDirection.VERTICAL ? Visibility.Visible : Visibility.None)
}
}
.alignItems(VerticalAlign.Center)
.linearGradient({ angle: 0, colors: [['#99000000', 0], ['#00000000', 1]] })
... ... @@ -313,19 +330,34 @@ export struct PlayUIComponent {
@Builder
playOrPauseBtn() {
//暂停、播放
Image(this.isPlayStatus ? $r('app.media.icon_live_player_pause') : $r('app.media.player_play_ic'))
.width(24)
.height(24)
.onClick(() => {
if (this.isPlayStatus) {
this.isPlayStatus = false
this.playerController?.pause()
} else {
if (this.playSourceState === PlayerConstants.STATUS_COMPLETION) {
//资源播放完成
Image($r('app.media.player_play_ic'))
.width(24)
.height(24)
.onClick(() => {
this.isPlayStatus = true
this.initPlayerSet()
this.playerController?.firstPlay(this.liveUrl)
this.playerController?.play()
}
})
})
} else {
//暂停、播放
Image(this.isPlayStatus ? $r('app.media.icon_live_player_pause') : $r('app.media.player_play_ic'))
.width(24)
.height(24)
.onClick(() => {
if (this.isPlayStatus) {
this.isPlayStatus = false
this.playerController?.pause()
} else {
this.isPlayStatus = true
this.playerController?.play()
}
})
}
}
@Builder
... ...
... ... @@ -13,7 +13,9 @@ const TAG: string = 'TopPlayComponent'
export struct TopPlayComponent {
@Consume @Watch('updateData') contentDetailData: ContentDetailDTO
playerController?: WDAliPlayerController
@State imgUrl: string = ''
// 预告片图片/视频url
@State previewUrl: string = ''
@State isVideoSource: boolean = false
//未开始
@State isWait: boolean = false
//已结束直播
... ... @@ -24,14 +26,15 @@ export struct TopPlayComponent {
@State isLoading: boolean = false
// 获取播放资源能播放了
@State isCanPlay: boolean = false
// 当前播放资源的状态
@Provide playSourceState: number = 0
private playUrl: string = ""
private xComponentIsLoaded: boolean = false
aboutToAppear(): void {
if (this.playerController) {
this.playerController.onCanplay = () => {
Logger.debug(TAG, 'onCanplay==>')
this.isCanPlay = true
this.isLoading = true
this.playerController?.play()
... ... @@ -40,35 +43,56 @@ export struct TopPlayComponent {
this.playerController.onStatusChange = (status: number) => {
this.playSourceState = status
Logger.debug(TAG, 'status==>' + status)
if (status === PlayerConstants.STATUS_ERROR) {
this.isError = true
this.isLoading = true
this.isCanPlay = false
} else if (status === PlayerConstants.STATUS_COMPLETION) {
// 播放完成
} else {
this.isError = false
}
}
}
this.updateData()
}
updateData() {
// 检测等待中的直播预告是否视频资源
if (this.contentDetailData.liveInfo && this.contentDetailData.liveInfo.previewType === 1
&& this.contentDetailData.liveInfo.previewUrl &&
this.contentDetailData.liveInfo.previewUrl.length > 0) {
// 预告资源是视频
this.isVideoSource = true
this.contentDetailData.showTime = true
}
//直播新闻-直播状态 wait待开播running直播中end已结束cancel已取消paused暂停
if (this.contentDetailData.liveInfo && this.contentDetailData.liveInfo.previewUrl &&
this.contentDetailData.liveInfo.previewUrl.length > 0) {
this.imgUrl = this.contentDetailData.liveInfo.previewUrl
Logger.debug(TAG, 'ok+' + `${this.imgUrl}`)
this.previewUrl = this.contentDetailData.liveInfo.previewUrl
} else if (this.contentDetailData.fullColumnImgUrls && this.contentDetailData.fullColumnImgUrls.length > 0) {
this.imgUrl = this.contentDetailData.fullColumnImgUrls[0].url
Logger.debug(TAG, 'ok-' + `${this.imgUrl}`)
this.previewUrl = this.contentDetailData.fullColumnImgUrls[0].url
}
this.isWait = this.contentDetailData?.liveInfo?.liveState == 'wait'
if(this.isWait ){
this.isLoading = true
Logger.debug(TAG, 'ok+' + `${this.previewUrl}`)
if (this.isVideoSource) {
this.isWait = false
this.isLoading = false
} else {
this.isWait = this.contentDetailData?.liveInfo?.liveState == 'wait'
if (this.isWait) {
this.isLoading = true
}
}
this.isEnd = this.contentDetailData?.liveInfo?.liveState === 'end' &&
StringUtils.isEmpty(this.contentDetailData?.liveInfo?.vlive[0]?.replayUri)
if (!this.isWait && this.contentDetailData.liveInfo && this.contentDetailData.liveInfo.vlive.length > 0) {
... ... @@ -78,12 +102,18 @@ export struct TopPlayComponent {
} else if (this.contentDetailData.liveInfo.liveState == 'end') {
playUrl = this.contentDetailData.liveInfo.vlive[0].replayUri
}
// this.playerController?.firstPlay('https://rmrbcmsonline.peopleapp.com/upload/rmh/video/mp4/202404/1713752415708fb81d0b8f137b.mp4');
if (StringUtils.isNotEmpty(playUrl)) {
Logger.debug(TAG, `${playUrl}`)
this.playUrl = playUrl
if (this.isVideoSource) {
this.playUrl = this.previewUrl
this.tryToPlay()
} else {
if (StringUtils.isNotEmpty(playUrl)) {
Logger.debug(TAG, `${playUrl}`)
this.playUrl = playUrl
this.tryToPlay()
}
}
}
}
... ... @@ -115,18 +145,22 @@ export struct TopPlayComponent {
.width('100%')
.visibility(this.isWait ? Visibility.None : Visibility.Visible)
// 直播房间图
Image(this.imgUrl)
.objectFit(ImageFit.Cover)
.visibility(this.isWait || this.isEnd ? Visibility.Visible : Visibility.None)
.contrast(this.isEnd ? 0.2 : 1)
.width('100%')
if (this.isVideoSource) {
} else {
// 直播房间图
Image(this.previewUrl)
.objectFit(ImageFit.Cover)
.visibility(this.isWait || this.isEnd ? Visibility.Visible : Visibility.None)
.contrast(this.isEnd ? 0.2 : 1)
.width('100%')
}
// loading
PictureLoading().visibility(this.isLoading ? Visibility.None : Visibility.Visible)
// 视频播放器上的控制面板和信息
PlayUIComponent({ playerController: this.playerController, isShowBottom: this.isCanPlay })
PlayUIComponent({ playerController: this.playerController, isShowBottom: this.isCanPlay, liveUrl: this.playUrl })
// 直播结束
Text('直播已结束')
... ...
... ... @@ -50,8 +50,6 @@ export struct WDPlayerRenderLiveView {
aboutToAppear() {
MGPlayRenderViewIns.add();
console.log('playerController', this.playerController)
insIndex++;
if (!this.playerController) {
return
... ... @@ -59,7 +57,6 @@ export struct WDPlayerRenderLiveView {
this.playerController.onVideoSizeChange = (width: number, height: number) => {
// console.log(`WDPlayerRenderView onVideoSizeChange width:${width} videoTop:${height}`)
Logger.info(TAG, ` onVideoSizeChange width:${width} videoTop:${height}`)
this.videoWidth = width;
this.videoHeight = height;
this.updateLayout()
... ... @@ -83,8 +80,6 @@ export struct WDPlayerRenderLiveView {
.onLoad(async (event) => {
Logger.info(TAG, 'onLoad')
let surfaceId = this.xComponentController.getXComponentSurfaceId()
console.log('surfaceId===', surfaceId)
console.log('insId===', this.insId)
this.xComponentController.setXComponentSurfaceSize({
surfaceWidth: 1920,
surfaceHeight: 720
... ...