| 类名 | 描述 |
|---|---|
| VHWatchVodPlayer | 播放器控制核心类 |
| VHWatchVodPlayerView | 播放器核心组件 |
| VHWatchVodPlayerCallback | 播放器事件回调 |
暖场视频用于在预约或直播前未开播的活动显示用户预设的视频。暖场视频配置通过调用初始化接口返回的VHWebinarData.VHWarmup类。并且满足部分条件才能进行观看。具体参考VHWarmup说明。
//是否满足暖场视频播放条件
private isEnablePlay(advance_play_time: number): boolean {
if (advance_play_time == 0) {
return true;
}
let date = new Date(this.webinar_info?.webinar?.start_time!);
let milliseconds = date.getTime();
let current = systemDateTime.getTime();
//未到开播时间
if (current <= milliseconds) {
let diff = milliseconds - current;
// 暖场视频播放
if (diff / 1000 < advance_play_time) {
return true;
} else {
return false;
}
}
//已经超过开播时间不播放暖场视频
return false;
}
//判断是否有暖场视频
if (this.webinar_info && this.webinar_info.warm_up) {
if (this.isEnablePlay(this.webinar_info.warm_up.advance_play_time)) {
Column() {
//构建暖场视频播放器
VHWarmPlayerView({ webinars: this.webinar_info, player_config: this.player_config })
.width('100%')
.height('100%')
}
.width('100%')
.flexShrink(0)
.zIndex(2)
.height('100%')
}
}
import { VHWatchVodPlayer} from "@vhall/vhall_live";
@Component
export struct VHWatchLivePlayerComponent {
@State message: string = 'Hello World';
/**
* 播放器控制类
* */
public vodPlayer?: VHWatchVodPlayer = new VHWatchVodPlayer(this.getUIContext().getHostContext() as Context);
......
}@Component
export struct VHWatchVodPlayerView {
/**
* 组件状态更新回调
*/
@Require public componentListener?: VHWatchVodPlayerCallback;
/**
* 播放器控制类
* */
@Require public vodPlayer?:VHWatchVodPlayer;
/**
* 控制是否扩展
*/
@Prop public is_expand: boolean = false;
/**
* @param enable_default_water_mask 是否使用默认水印
* **/
@Require enable_default_water_mask:boolean = false;
/**
* @param water 水印
* **/
@Require water: VHWatermarkConfig | null = null;
} build() {
Stack({ alignContent: Alignment.Center }) {
// 播放器容器
VHWatchVodPlayerView({
componentListener: this,
vodPlayer: this.vodPlayer,
is_expand: this.is_expand,
enable_default_water_mask: true,
water: this.player_config?.water
})
.width('100%')
.height('100%')
.expandSafeArea(this.is_expand ? this.expandTypes : [], this.is_expand ? this.expandEdges : [])
.backgroundColor(Color.Black)
.align(Alignment.Center)
.zIndex(playerZIndex.indexOf(PLAYER_INDEX))
}
} public initWebinarInfo(webinarsInfo: VHWebinarData)| 参数名称 | 是否必须 | 示例 | 备注 |
|---|---|---|---|
| webinarsInfo | 是 | xx | 活动信息 |
| 代码示例 |
aboutToAppear(): void {
this.vodPlayer?.initWebinarInfo(this.webinars!);
} public setLivePlayerListener(listener: VHWatchLivePlayerCallback)
@Component
export struct VHWatchVodPlayerComponent {
public vodPlayer?: VHWatchVodPlayer = new VHWatchVodPlayer(this.getUIContext().getHostContext() as Context);
@State public webinars: VHWebinarData | null = null;
aboutToAppear(): void {
//初始化播放器活动信息
this.vodPlayer?.initWebinarInfo(this.webinars!);
//是否支持画中画
if (this.player_config?.basic?.picture_in_picture == 1) {
this.vodPlayer?.initPipController(true);
}
this.vodPlayer?.setVodPlayerListener(this);
this.vodPlayer?.setPlayerScalingMode(VHPlayerVideoScalingMode.VH_ASPECT_FILL);
}
} public startPlayWithRecordId(paas_record_id:string,def:VHPlayDefinition)| 参数名称 | 是否必须 | 示例 | 备注 |
|---|---|---|---|
| paas_record_id | 是 | xx | 回放id,通过初始化接口获取到warmup_paas_record_id数组得到回放id, |
| def | 是 | xx | 清晰度 |
//组件初始化成功后播放暖场视频
onPlayerViewInitCompleted(){
if( this.currentPlayId.length > 0){
this.vodPlayer?.startPlayWithRecordId(this.currentPlayId,this.player_config?.default_definition!);
this.isPlayError = true;
}
} public stopPlay() this.vodPlayer?.stopPlay(); public pausePlay() //控制暂停和恢复
if(this.is_playing){
this.vodPlayer?.pausePlay();
this.vodPlayer?.updatePipControlStatus(VHPipControlPanelStatus.VH_CONTROL_PAUSE);
}else{
this.vodPlayer?.resumePlay();
this.vodPlayer?.updatePipControlStatus(VHPipControlPanelStatus.VH_CONTROL_PLAY);
} public resumePlay() //控制暂停和恢复
if(this.is_playing){
this.vodPlayer?.pausePlay();
this.vodPlayer?.updatePipControlStatus(VHPipControlPanelStatus.VH_CONTROL_PAUSE);
}else{
this.vodPlayer?.resumePlay();
this.vodPlayer?.updatePipControlStatus(VHPipControlPanelStatus.VH_CONTROL_PLAY);
} public setSpeed(speed:VHVodSupportSpeed)| 参数名称 | 是否必须 | 示例 | 备注 |
|---|---|---|---|
| speed | 是 | xx | 倍速枚举 VHVodSupportSpeed |
| 代码示例 |
//控制暂停和恢复
builder: SpeedPopup({
onItemSelected: (item: popItemValueObj) => {
if (this.is_start) {
this.vodPlayer?.setSpeed(item.value as VHVodSupportSpeed)
this.speedSetting = item.value as VHVodSupportSpeed;
}
this.speed = item.text;
this.isSpeedShow = false;
}
}) public resumePlay() //控制暂停和恢复
if(this.is_playing){
this.vodPlayer?.pausePlay();
this.vodPlayer?.updatePipControlStatus(VHPipControlPanelStatus.VH_CONTROL_PAUSE);
}else{
this.vodPlayer?.resumePlay();
this.vodPlayer?.updatePipControlStatus(VHPipControlPanelStatus.VH_CONTROL_PLAY);
} public seek(currentTime:number)| 参数名称 | 是否必须 | 示例 | 备注 |
|---|---|---|---|
| currentTime | 是 | xx | 播放位置 (ms) |
| 代码示例 |
//控制暂停和恢复
this.vodPlayer?.seekCasting(this.currentTime); public changeDefinition(def: VHPlayDefinition)| 参数名称 | 是否必须 | 示例 | 备注 |
|---|---|---|---|
| def | 是 | xx | 清晰度 |
//根据选择的清晰度进行播放
builder:definitionsPopup({onItemSelected: (item: popItemValueObj) => {
if (this.is_start) {
this.isLoading = true;
this.vodPlayer?.changeDefinition(item.value as VHPlayDefinition)
this.isPlayError = true;
}
this.playDefinition = item.text;
this.isDefinition = false;
}},this.playDefinitionList), public setPlayerVolume(vol: number): number| 参数名称 | 是否必须 | 示例 | 备注 |
|---|---|---|---|
| vol | 是 | xx | 范围:0.01 ~ 1.0 |
//根据选择的清晰度进行播放
.onActionUpdate((event: GestureEvent | undefined) => {
if (event && event.fingerList && event.fingerList[0]) {
const touchY = event.fingerList[0].localY;
const deltaY = touchY - touchStartY;
// 移动距离占播放器高度的比例(deltaY取反是因为值的正负与滑动方向相反)
const percent = (-deltaY / changeHeight);
const width = changeWidth as number;
if (touchStartX <= (width / 2)) {
} else {
let v = this.playerVol - (event.offsetY)/3/10;
let newVol = this.playerVol + percent;
// 严格限制在0-100范围内
newVol = Math.max(0, Math.min(100, v));
this.playerVol = newVol;
this.isShowVol = true;
this.vodPlayer?.setPlayerVolume(newVol/100);//设置音量
}
touchStartY = touchY;
}
}) public setPlayerScalingMode(mode: VHPlayerVideoScalingMode)| 参数名称 | 是否必须 | 示例 | 备注 |
|---|---|---|---|
| mode | 是 | xx | VHPlayerVideoScalingMode 图像拉伸,等比例缩放,平铺 |
//根据选择的清晰度进行播放
aboutToAppear(): void {
this.vodPlayer?.initWebinarInfo(this.webinars!);
if(this.playerConfig?.basic?.picture_in_picture == 1){
this.vodPlayer?.initPipController(true);
}
this.vodPlayer?.setLivePlayerListener(this);
this.vodPlayer?.setPlayerScalingMode(VHPlayerVideoScalingMode.VH_ASPECT_FILL);
} public destroyPlayer() aboutToDisappear(): void {
this.barrageController.pause();
this.barrageController.clearInScreen();
this.vodPlayer?.destroyPlayer();
} public initCastPlay(source: VHCastMediaSource) aboutToAppear(): void {
this.vodPlayer?.initWebinarInfo(this.webinars!);
this.vodPlayer?.setLivePlayerListener(this);
this.vodPlayer?.setPlayerScalingMode(VHPlayerVideoScalingMode.VH_ASPECT_FILL);
this.source = {
title:this.webinars?.webinar?.subject!,
name:'微吼SaaS',
description:this.webinars?.webinar?.subject!,
headImage:"https://cnstatic01.e.vhall.com/.....c1651.jpg"
};
//初始化投屏后,会创建媒体会话绑定播控服务。如果不使用投屏功能不要进行初始化。
this.vodPlayer?.initCastPlay(this.source);
}); public startCastingPlay() 使用投播控件自动实现投屏 public seekCasting(seekTime: number) | 参数名称 | 是否必须 | 示例 | 备注 |
|---|---|---|---|
| seekTime | 是 | xx | 播放进度,单位ms. |
this.vodPlayer?.seekCasting(10001); public stopCastingPlay() .onClick(() => {
this.vodPlayer?.stopCastingPlay();
this.isCasting = false;
}) public initPipController(autoStart:boolean,navId?:string)| 参数名称 | 是否必须 | 示例 | 备注 |
|---|---|---|---|
| autoStart | 是 | xx | boolean 退到后台是否自动开启画中画 |
| navId | 否 | xx | 1、UIAbility使用Navigation管理页面,需要设置Navigation控件的id属性,并将该id设置给画中画控制器,确保还原场景下能够从画中画窗口恢复到原页面; 2、UIAbility使用Router管理页面时(画中画场景不推荐该导航方式),无需设置navigationId。注意:该场景下启动画中画后,不要进行页面切换,否则还原场景可能出现异常; 3、UIAbility只有单页面时,无需设置navigationId,还原场景下也能够从画中画窗口恢复到原页面 |
aboutToAppear(): void {
//如果活动配置支持了画中画则进行配置
this.vodPlayer?.initWebinarInfo(this.webinars!);
if(this.playerConfig?.basic?.picture_in_picture == 1){
this.vodPlayer?.initPipController(true);
}
} public startPiP() this.vodPlayer?.startPiP(); public updatePipControlStatus(status: VHPipControlPanelStatus)| 参数名称 | 是否必须 | 示例 | 备注 |
|---|---|---|---|
| status | 是 | xx | VHPipControlPanelStatus |
if(this.is_playing){
this.vodPlayer?.pausePlay();
this.vodPlayer?.updatePipControlStatus(VHPipControlPanelStatus.VH_CONTROL_PAUSE);
}else{
this.vodPlayer?.resumePlay();
this.vodPlayer?.updatePipControlStatus(VHPipControlPanelStatus.VH_CONTROL_PLAY);
} public stopPiP() this.vodPlayer?.stopPiP();
onPlayerViewInitCompleted() {
this.vodPlayer?.startPlay(this.player_config?.default_definition!);
this.isPlayError = true;
}
/**
* 播放器播放失败事件
*/
onPlayerError(error: VHErrorInfo) {
if (error.code == VHConstants.VH_PLAY_CURRENT_DEF_FAILED) {
//没有可用的清晰度,重新进行播放。
this.vodPlayer?.startPlay(this.player_config?.default_definition!);
} else if (error.code == VHConstants.VH_PLAY_STREAM_FAILED || error.code == VHConstants.VH_NETWORK_ERROR ||
error.code == VHConstants.VH_CAN_NOT_FIND_HOST) {
this.vodPlayer?.pausePlay();
this.is_playing = false;
this.isPlayError = true;
}
this.getUIContext().getPromptAction().showToast({
message: error.code + error.message,
duration: 4000,
showMode: promptAction.ToastShowMode.DEFAULT,
bottom: 80
});
}
/**
* 观看状态回调
* @param player 播放器实例
* @param state 状态类型 详见 VHPlayerStatus 的定义.
*/
onStatusDidChange(state: VHPlayerState) {
if (state == VHPlayerState.VH_PLAYER_PLAYING) {
this.is_start = true;
this.is_playing = true;
this.isLoading = false;
this.isPlayError = false;
} else if (state == VHPlayerState.VH_PLAYER_PAUSE || state == VHPlayerState.VH_PLAYER_STOP ||
state == VHPlayerState.VH_PLAYER_RELEASE) {
this.is_playing = false;
}
if (state == VHPlayerState.VH_PLAYER_PREPARED) {
this.isLoading = true;
if (this.isPlayError) {
//播放失败后重新播放监听VH_PLAYER_PREPARED事件,然后重置下播放进度
this.vodPlayer?.seek(this.currentTime);
}
} else if (state == VHPlayerState.VH_PLAYER_BUFFERING_END) {
this.isLoading = false;
}
this.playerState = state;
}
/**
* 画中画状态
* @param state: 画中画播放状态
*/
onPipStatusChange(state: VHPlayerPipState) {
}
/**
* 画中画控制中心播放状态
* @param state: VHPlayerPipControlPanelStatus
*/
onPipControlPanelStatusChange(state: VHPipControlPanelStatus) {
if (state == VHPipControlPanelStatus.VH_CONTROL_PAUSE) {
this.vodPlayer?.pausePlay();
} else {
this.vodPlayer?.resumePlay();
}
}
/**
* 播放器音量
* @param volume :当前播放器音量值
*/
onPlayerVolume(volume: number) {
}
/**
* 当前房间支持的清晰度列表
* @param definitions 支持的清晰度列表
*/
onValidDefinitions(definitions: VHPlayDefinition[]) {
let hasDef: boolean = false;
this.definitions_list = [];
this.playDefinitionList = [];
definitions.forEach((item) => {
//是否隐藏原画
if (item == VHPlayDefinition.VH_ORIGIN && this.configList.live_hidden_same == 1) {
return;
}
this.definitions_list.push(item);
if (this.player_config?.default_definition == item) {
hasDef = true;
}
this.resetPlayDef(item);
})
if (!hasDef && this.player_config && definitions.length > 0) {
definitions.forEach((item) => {
//如果没有默认清晰度,则重新选择一个清晰度
if (item != VHPlayDefinition.VH_AUDIO && this.player_config) {
this.resetPlayDef(item);
this.player_config.default_definition = item as VHPlayDefinition;
return;
}
})
}
}
/**
* 视频流播放成功后,回调视频流的宽髙。用户可根据视频流实际宽高,调整播放容器大小。
* @param width 视频帧宽度
* @param height 视频帧高度
*/
onVideoFrameSize(width: number, height: number) {
}
/**
* 投屏设备状体事件回调。
* @param state. 0 设备已准备好可以播放。1:设备已断开
*/
onCastPlayDeviceState(state: number) {
this.isCasting = state == 0 ? true : false;
}
/**
* 已连接的投屏设备。
* @param state. 0 设备已准备好可以播放。1:设备已断开
*/
onCastPlayDeviceConnected(device: VHCastPickerDevice) {
this.castPickDevice = device.deviceName;
}
/**
* 投屏事件回调。
* @param state:VHCastPlayState
*/
onCastPlayState(state: VHCastPickerState) {
if (state == VHCastPickerState.VH_CAST_PICKER_STATE_PLAY) {
this.is_start = true;
this.is_playing = true;
} else if (state == VHCastPickerState.VH_CAST_PICKER_STATE_PAUSE ||
state == VHCastPickerState.VH_CAST_PICKER_STATE_STOP) {
this.is_playing = false;
}
}
/**
* 投屏播放时长。
* @param duration:number 播放总时长 ,单位ms
*/
onCastPlayDuration(duration: number) {
this.totalDuration = duration;
}
/**
* 投屏播放当前位置。
* @param position:number 播放当前进度 ,单位ms
*/
onCastPlayPosition(position: number) {
this.currentTime = position;
}
/**
* 投屏操作失败。
* @param state:VHCastPlayState。执行相关操作失败。包括开播、暂停、设置进度
*/
onCastPlayError(state: VHCastPickerState) {
this.getUIContext().getPromptAction().showToast({
message: '投屏异常' + state.toString(),
duration: 4000,
showMode: promptAction.ToastShowMode.DEFAULT,
bottom: 80
});
}
/**
* @description 监听资源播放资源的时长,单位为毫秒(ms),用于刷新进度条长度
* @param duration 回放时长,单位为毫秒(ms)。
*/
onVodTotalDuration(duration: number) {
this.totalDuration = duration;
}
/**
* @description 当前播放时长,单位为毫秒(ms),用于刷新进度条长度
* @param currentDuration 当前播放时长,单位为毫秒(ms)。
*/
onVodCurrentDuration(current: number) {
this.currentTime = current;
//定时每秒同步播放进度。用于同步显示实时字幕
if (this.subtitleTimer == 0) {
this.subtitleTimer = setInterval(() => {
let msgData: EmitterMsgData;
msgData = {
type: MsgType.VOD_SUBTITLE_TIME,
data: this.currentTime
} as EmitterMsgData;
EmitterUtil.send(msgData);
}, 500)
}
}
/**
* @description 监听SeekTime,用于刷新进度条长度
* @param duration 当前时长,单位为毫秒(ms)。
*/
onVodSeekDuration(duration: number) {
}
/**
* @description 监听倍速切换,表示切换倍速成功。
* @param speed 当前倍速
*/
onVodSpeedDone(speed: VHVodSupportSpeed) {
}| VHWarmup | 类型 | 备注 |
|---|---|---|
| warmup_paas_record_id | string[] | 暖场paas_record_id |
| warmup_img_url | string | 暖场封面 |
| warmup_player_type | number | 1:单次播放 2:循环播放 |
| advance_play_time | number | 提前播放时间,单位秒. 预约或开播前展示 |