HarmonyOS 鸿蒙Next视频投播的开发总结
HarmonyOS 鸿蒙Next视频投播的开发开发准备:DevEcostudio, 真机,视频资源列表。
投播借助于媒体会话和投播组件两种方式来寻找合适的投播设备,具体实现方式如下
- 视频资源加入会话列表;
1.2 会话创建成功后,开启支持投播,关键代码:
this.session.setExtras({
requireAbilityList: ['url-cast'],
})
1.3 会话创建成功后,监听投播事件:
outputDeviceChange
1.4 在播放视频资源的时候,可以点击投播组件,寻找合适的投播设备:
点击投播组件,可以把正在播放的视频资源投播到合适的设备上, 关键代码:
outputDeviceChange-playItem函数
import { media } from '[@kit](/user/kit).MediaKit';
import { common, wantAgent } from '[@kit](/user/kit).AbilityKit';
import { avSession } from '[@kit](/user/kit).AVSessionKit';
import { formProvider } from '[@kit](/user/kit).FormKit';
import { formBindingData } from '[@kit](/user/kit).FormKit';
import { BusinessError, emitter } from '[@kit](/user/kit).BasicServicesKit';
import { SongConstants } from '../model/SongConstants';
import { AudioPlayerState, MusicPlayMode } from '../model/MusicData';
import { SongItem } from '../model/SongData';
import { CardData } from '../model/CardData';
import { LessonsBriefIntroduction } from '../model/HearingDetailModule';
import { PreferencesUtil } from './PreferencesUtil';
import { MediaTools } from './MediaTools';
import { DataConstants } from '../const/DataConstants';
const TAG = 'MediaService';
export class MediaService {
private context: common.UIAbilityContext = getContext() as common.UIAbilityContext;
public avPlayer?: media.AVPlayer;
private session?: avSession.AVSession;
castController: avSession.AVCastController | undefined = undefined;
private songItem: SongItem = new SongItem();
private playMode: MusicPlayMode = MusicPlayMode.ORDER;
private state: AudioPlayerState = AudioPlayerState.IDLE;
private isFirst: boolean = true;
private isPrepared: boolean = false;
public songList: SubscribedAbstractProperty<SongItem[]> | null = null;
private isPlay: SubscribedAbstractProperty<boolean> | null = null;
private musicIndex: SubscribedAbstractProperty<number> | null = null;
public audioId: SubscribedAbstractProperty<number> | null = null;
public currentTime: SubscribedAbstractProperty<number> | null = null;
private progress: SubscribedAbstractProperty<number> | null = null;
private playAll: SubscribedAbstractProperty<boolean> | null = null;
private pic: SubscribedAbstractProperty<string> | null = null;
private isVip: SubscribedAbstractProperty<boolean> | null = null;
private sort: SubscribedAbstractProperty<string> | null = null;
private audioDetail: SubscribedAbstractProperty<LessonsBriefIntroduction> | null = null;
private progressMax: SubscribedAbstractProperty<number> | null = null;
private totalTime: SubscribedAbstractProperty<string> | null = null;
private selectIndex: SubscribedAbstractProperty<number> | null = null;
private mediaService: SubscribedAbstractProperty<MediaService> | null = null;
private institutionId: SubscribedAbstractProperty<string> | null = null;
private formIds: string[] = [];
private isCurrent: boolean = true;
private speed: media.PlaybackSpeed = media.PlaybackSpeed.SPEED_FORWARD_1_00_X;
private seekCall: (seekDoneTime: number) => void = (seekDoneTime: number) => {
this.isCurrent = true;
console.log(TAG, `AVPlayer seek succeeded, seek time is ${seekDoneTime}`);
this.setPlayState({
position: {
elapsedTime: this.getCurrentTime(),
updateTime: new Date().getTime()
}
});
};
private errorCall: (err: BusinessError) => void = (err: BusinessError) => {
console.error(TAG, `Invoke avPlayer failed, code is ${err.code}, message is ${err.message}`);
this.avPlayer?.reset();
};
private updateTimeCall: (updateTime: number) => void = (updateTime: number) => {
let musicIndexNum = this.musicIndex?.get();
if (this.isCurrent && musicIndexNum != null) {
let list = this.songList?.get() as SongItem[]
list[musicIndexNum].play_time = updateTime;
list[musicIndexNum].isFinish = this.songItem.isFinish;
this.songList?.set(list)
this.currentTime?.set(updateTime)
this.progress?.set(updateTime)
// AppStorage.setOrCreate('currentTime', MediaTools.msToCountdownTime(updateTime));
// AppStorage.setOrCreate<number>('progress', updateTime);
}
};
private stateCall: (state: string) => Promise<void> = async (state: string) => {
let musicIndexNum = this.musicIndex?.get()
switch (state) {
case 'idle':
const time = this.currentTime?.get()
console.info(TAG, 'AVPlayer state idle called.');
this.state = AudioPlayerState.IDLE;
let songListArray = this.songList?.get() as SongItem[]
if (musicIndexNum != null) {
this.songItem = JSON.parse(JSON.stringify(songListArray[musicIndexNum]));
}
let url = this.songItem.src;
if (this.avPlayer && url) {
// let avFileDescriptor: media.AVFileDescriptor = { fd: url.fd, offset: url.offset, length: url.length };
this.avPlayer.url = url;
// console.info(TAG, 'loadAsset avPlayer.url:' + this.avPlayer.fdSrc);
}
break;
case 'initialized':
console.info(TAG, 'AVPlayer state initialized called.');
this.state = AudioPlayerState.INITIALIZED;
if (this.avPlayer) {
this.avPlayer.prepare().then(() => {
console.info(TAG, 'AVPlayer prepare succeeded.');
}, (err: BusinessError) => {
console.error(TAG, `Invoke prepare failed, code is ${err.code}, message is ${err.message}`);
});
}
break;
case 'prepared':
console.info(TAG, 'AVPlayer state prepared called.');
this.state = AudioPlayerState.PREPARED;
this.isPrepared = true;
this.totalTime?.set(MediaTools.msToCountdownTime(this.getDuration()))
this.progressMax?.set(this.getDuration())
// AppStorage.setOrCreate('totalTime', MediaTools.msToCountdownTime(this.getDuration()));
// AppStorage.setOrCreate('progressMax', this.getDuration());
// let isNeedVip = (this.audioDetail?.get().is_vip == '2' && !AppStorage.get<boolean>('userVip'))
// let isNeedBuyT = (Number(this.audioDetail?.get().audio_price) != 0 && this.audioDetail?.get().is_buy == '0')
let listenByBuy = this.audioDetail?.get().is_buy == '1';
let listenByVip = this.audioDetail?.get().is_vip == '2' && AppStorage.get<boolean>('userVip');
let isNeedBuy = (Number(this.audioDetail?.get().audio_price) != 0 && !(listenByBuy || listenByVip));
let isNeedVip = (this.audioDetail?.get().is_vip == '2' && !(AppStorage.get<boolean>('userVip') || listenByBuy));
let isAudition = this.songItem.is_audition == '1'
if ((isNeedVip || isNeedBuy) && !isAudition && !(Number(this.institutionId?.get()) > 1)) {
return;
}
if (this.avPlayer) {
let time: number = this.songItem?.play_time
this.avPlayer.play().then(() => {
if (time != Number(this.songItem?.duration)) {
this.seek(time)
}
})
}
this.setAVMetadata();
console.info(TAG, 'AVPlayer prepared succeeded.');
break;
case 'playing':
console.info(TAG, 'AVPlayer state playing called.');
if (this.avPlayer) {
this.avPlayer.setSpeed(this.speed);
}
this.state = AudioPlayerState.PLAY;
break;
case 'paused':
console.info(TAG, 'AVPlayer state paused called.');
this.state = AudioPlayerState.PAUSE;
break;
case 'completed':
console.info(TAG, 'AVPlayer state completed called.');
this.state = AudioPlayerState.COMPLETED;
let list = this.songList?.get() as SongItem[]
if (this.songItem.isFinish) {
if (musicIndexNum != null) {
list[musicIndexNum].isFinish = false;
}
this.songItem.isFinish = false;
this.avPlayer?.play()
return;
} else {
if (musicIndexNum != null) {
list[musicIndexNum].isFinish = true;
}
}
this.playNextAuto(false);
break;
case 'stopped':
console.info(TAG, 'AVPlayer state stopped called.');
this.state = AudioPlayerState.STOP;
if (this.avPlayer) {
this.avPlayer.reset();
}
break;
case 'released':
console.info(TAG, 'AVPlayer state released called.');
this.state = AudioPlayerState.RELEASED;
break;
default:
console.info(TAG, 'AVPlayer state unknown called.');
this.state = AudioPlayerState.UNKNOWN;
break;
}
this.updateCardData();
this.updateIsPlay(this.state === AudioPlayerState.PLAY);
};
private playCall: () => void = () => {
console.info(TAG, `on play , do play task`);
if (this.isFirst) {
this.loadAssent(0);
} else {
this.play();
}
};
private pauseCall: () => void = () => {
console.info(TAG, `on pause , do pause task`);
this.pause();
};
private playNextCall: () => void = () => {
console.info(TAG, `on playNext , do playNext task`);
this.playNextAuto(true);
};
private playPreviousCall: () => void = () => {
console.info(TAG, `on playPrevious , do playPrevious task`);
this.playPrevious();
};
private putDeviceChangeCall: (
connectState: avSession.ConnectionState,
device: avSession.OutputDeviceInfo
) => void = async (
connectState: avSession.ConnectionState,
device: avSession.OutputDeviceInfo
) => {
let currentDevice: avSession.DeviceInfo = device?.devices?.[0];
if (currentDevice.castCategory === avSession.AVCastCategory.CATEGORY_REMOTE &&
connectState === avSession.ConnectionState.STATE_CONNECTED) { // 设备连接成功
console.info(`Device connected: ${device}`);
this.castController = await this.session?.getAVCastController();
console.info('Succeeded in getting a cast controller');
// 查询当前播放的状态
let avPlaybackState = await this.castController?.getAVPlaybackState();
console.info(`Succeeded in AVPlaybackState resource obtained: ${avPlaybackState}`);
// 监听播放状态的变化
this.castController?.on('playbackStateChange', 'all', (state: avSession.AVPlaybackState) => {
console.info(`Succeeded in Playback state changed: ${JSON.stringify(state)}`);
});
if (currentDevice.supportedProtocols === avSession.ProtocolType.TYPE_CAST_PLUS_STREAM) {
// 此设备支持cast+投播协议
this.playItem()
} else if (currentDevice.supportedProtocols === avSession.ProtocolType.TYPE_DLNA) {
// 此设备支持DLNA投播协议
this.playItem()
}
}
}
private stopTimer: number = 0;
private closeTime: SubscribedAbstractProperty<number> | null = null;
constructor(audioId?: number) {
let arr: SongItem[] = []
this.songList = AppStorage.setAndLink('songList', arr);
this.isPlay = AppStorage.setAndLink('isPlay', false);
this.musicIndex = AppStorage.setAndLink('musicIndex', 0);
this.audioId = AppStorage.setAndLink('audioId', audioId ? audioId : 0);
this.currentTime = AppStorage.setAndLink('currentTime', 0)
this.progress = AppStorage.setAndLink('progress', 0)
this.playAll = AppStorage.setAndLink('playAll', true)
this.pic = AppStorage.setAndLink('pic', '')
this.isVip = AppStorage.setAndLink('isVip', false)
this.sort = AppStorage.setAndLink('sort', '0')
this.totalTime = AppStorage.setAndLink('totalTime', '')
this.progressMax = AppStorage.setAndLink('progressMax', 0)
this.closeTime = AppStorage.setAndLink('closeTime', 0)
this.selectIndex = AppStorage.setAndLink('selectIndex', 0)
this.audioDetail = AppStorage.setAndLink('audioDetail', new LessonsBriefIntroduction())
this.institutionId = AppStorage.setAndLink('institutionId', '1')
switch (AppStorage.get('hearPlayMode')) {
case 'order':
this.playMode = MusicPlayMode.ORDER;
break;
case 'single':
this.playMode = MusicPlayMode.SINGLE_CYCLE;
break;
}
let speedIndex = AppStorage.get<number>('hearSpeedIndex')
this.speed = DataConstants.SPEED_LIST[speedIndex ?? 0]
}
public getPic(pic: string) {
this.pic?.set(pic)
}
public getItem() {
return this.songItem;
}
public getItemByIndex(index: number) {
let list = this.songList?.get()
if (list && index < list?.length) {
return list[index];
}
return this.songItem;
}
public setSpeed(speed: media.PlaybackSpeed) {
this.speed = speed
if (this.avPlayer?.state != 'idle') {
this.avPlayer?.setSpeed(speed)
}
}
public static getInstance(): MediaService {
let mediaService: MediaService | undefined = AppStorage.get('mediaService');
if (!mediaService) {
mediaService = new MediaService();
AppStorage.setOrCreate('mediaService', mediaService);
}
return mediaService;
}
public getSessionList(list: SongItem[]) {
this.songList?.set(list)
}
public initAudioPlayer() {
media.createAVPlayer().then(async avPlayer => {
if (avPlayer !== null) {
this.avPlayer = avPlayer;
this.setAVPlayerCallback();
this.createSession();
}
}).catch((error: BusinessError) => {
console.error(TAG, 'this avPlayer: ', `catch error happened,error code is ${error.code}`)
})
}
public saveStatus() {
let data: emitter.EventData = {
data: {
detail: JSON.stringify(this.songList?.get()),
audioDetail: JSON.stringify(this.audioDetail?.get()),
id: this.audioId?.get(),
hearingIndex: this.musicIndex?.get(),
sort: this.sort?.get(),
}
}
emitter.emit('updateHearingDetail', data);
}
private addSave() {
emitter.once('queryHearingDetailByIDBack', () => {
let detail: string = (data.data)?.['detail']
if (!detail) {
let data: emitter.EventData = {
data: {
detail: JSON.stringify(this.songList?.get()),
audioDetail: JSON.stringify(this.audioDetail?.get()),
hearingId: this.audioId?.get(),
hearingIndex: this.musicIndex?.get(),
hearingCover: this.pic?.get(),
}
}
emitter.emit('addHearingDraft', data);
}
})
let data: emitter.EventData = {
data: {
id: this.audioId?.get()
}
}
emitter.emit('queryHearingDetailByID', data);
}
private setAVPlayerCallback() {
console.log(`avplaerstate+${this.avPlayer?.state}`)
if (!this.avPlayer) {
return;
}
this.avPlayer.on('seekDone', this.seekCall);
this.avPlayer.on('error', this.errorCall);
this.avPlayer.on('timeUpdate', this.updateTimeCall);
this.avPlayer.on('stateChange', this.stateCall)
this.addSave()
}
async createSession() {
if (!this.context) {
return;
}
this.session = await avSession.createAVSession(this.context, 'CHILD_DUBBING', 'audio');
this.session.activate();
console.info(TAG, `session create done : sessionId : ${this.session.sessionId}`);
this.session.setExtras({
requireAbilityList: ['url-cast'],
});
this.setAVMetadata();
let wantAgentInfo: wantAgent.WantAgentInfo = {
wants: [
{
bundleName: this.context.abilityInfo.bundleName,
abilityName: this.context.abilityInfo.name,
}
],
requestCode: 0,
actionType: wantAgent.OperationType.START_ABILITIES,
actionFlags: [wantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG],
}
wantAgent.getWantAgent(wantAgentInfo).then((agent) => {
if (this.session) {
this.session.setLaunchAbility(agent);
}
})
this.setListenerForMesFromController();
}
async setListenerForMesFromController() {
if (!this.session) {
return;
}
this.session.on('play', this.playCall);
this.session.on('pause', this.pauseCall);
this.session.on('playNext', this.playNextCall);
this.session.on('playPrevious', this.playPreviousCall);
this.session.on('outputDeviceChange', this.putDeviceChangeCall)
}
async unregisterSessionListener() {
if (!this.session) {
return;
}
}
async setAVMetadata() {
let musicIndexNum = this.musicIndex?.get()
let id = this.musicIndex;
let songListArray = this.songList?.get() as SongItem[]
try {
if (this.context && musicIndexNum != null) {
// let mediaImage = await MediaTools.getPixelMapFromResource(this.context,
// this.songList[this.musicIndex].label as resourceManager.Resource);
// console.info(TAG, 'getPixelMapFromResource success' + JSON.stringify(mediaImage));
let metadata: avSession.AVMetadata = {
assetId: `${id}`,
title: songListArray[musicIndexNum].title,
artist: songListArray[musicIndexNum].singer,
mediaImage: songListArray[musicIndexNum].label,
// filter: avSession.ProtocolType.TYPE_CAST_PLUS_STREAM|avSession.ProtocolType.TYPE_DLNA,
duration: this.getDuration()
};
if (this.session) {
this.session.setAVMetadata(metadata).then(() => {
console.info(TAG, 'SetAVMetadata successfully');
}).catch((err: BusinessError) => {
console.error(TAG, `SetAVMetadata BusinessError: code: ${err.code}, message: ${err.message}`);
});
}
}
} catch (error) {
console.error(TAG, `SetAVMetadata try: code: ${(error as BusinessError).code},
message: ${(error as BusinessError).message}`);
}
}
/**
* Play music by index.
*
* @param musicIndex
*/
public async loadAssent(musicIndex: number) {
let musicIndexNum = this.musicIndex?.get()
let songListArray = this.songList?.get() as SongItem[]
if (musicIndex >= songListArray.length) {
console.error(TAG, `current musicIndex ${musicIndex}`);
return;
}
// BackgroundUtil.startContinuousTask(this.context);
this.updateMusicIndex(musicIndex);
if (this.isFirst && this.avPlayer && musicIndexNum != null) {
this.isFirst = false;
// this.songItem = await this.songItemBuilder.build(this.songList[this.musicIndex]);
this.songItem = songListArray[musicIndexNum];
let url = this.songItem.src;
if (url) {
this.avPlayer.url = url;
// console.info(TAG, 'loadAsset avPlayer.url:' + this.avPlayer.fdSrc);
}
} else {
await this.stop();
}
}
/**
* Get whether the music is played for the first.
*
* @returns isFirst
*/
public getFirst() {
return this.isFirst;
}
/**
* Set music play mode.
*
* @param playMode
*/
public setPlayModel(playMode: MusicPlayMode) {
this.playMode = playMode;
console.info(TAG, 'setPlayModel mode: ' + this.playMode);
}
/**
* Get music play mode.
*
* @returns playMode.
*/
public getPlayMode(): MusicPlayMode {
return this.playMode;
}
// 切换播放状态
private updateIsPlay(isPlay: boolean) {
this.isPlay?.set(isPlay)
this.setPlayState({
state: isPlay ? avSession.PlaybackState.PLAYBACK_STATE_PLAY : avSession.PlaybackState.PLAYBACK_STATE_PAUSE,
position: {
elapsedTime: this.getCurrentTime(),
updateTime: new Date().getTime()
}
});
}
/**
* Seek play music.
*
* @param ms.
*/
public seek(ms: number) {
if (this.isPrepared && this.state != AudioPlayerState.ERROR && this.avPlayer) {
let seekMode = this.getCurrentTime() < ms ? 0 : 1;
let realTime = (ms <= 0 ? 0 : (ms >= this.getDuration() ? this.getDuration() : ms));
this.isCurrent = false;
this.avPlayer.seek(realTime, seekMode);
}
}
private getCurrentTime() {
if (this.isPrepared && this.avPlayer) {
return this.avPlayer.currentTime;
}
return 0;
}
private getDuration() {
if (this.isPrepared && this.avPlayer) {
return this.avPlayer.duration;
}
return 0;
}
private start(seekMs?: number) {
console.info(TAG, 'AVPlayer play() isPrepared:' + this.isPrepared + ', state:' + this.state + ',seek:' + seekMs);
if (this.avPlayer) {
let songListArray: SongItem[] | undefined = this.songList?.get()
let musicIndexNum: number | undefined = this.musicIndex?.get()
if (songListArray?.length && musicIndexNum) {
this.avPlayer.url = songListArray[musicIndexNum].lesson_audio
}
}
}
/**
* Play music.
*/
public async play() {
console.info(TAG, 'AVPlayer play() isPrepared:' + this.isPrepared + ', state:' + this.state);
// BackgroundUtil.startContinuousTask(this.context);
let listenByBuy = this.audioDetail?.get().is_buy == '1';
let listenByVip = this.audioDetail?.get().is_vip == '2' && AppStorage.get<boolean>('userVip');
let isNeedBuy = (Number(this.audioDetail?.get().audio_price) != 0 && !(listenByBuy || listenByVip));
let isNeedVip = (this.audioDetail?.get().is_vip == '2' && !(AppStorage.get<boolean>('userVip') || listenByBuy));
let isAudition = this.songItem.is_audition == '1'
if ((isNeedVip || isNeedBuy) && !isAudition && !(Number(this.institutionId?.get()) > 1)) {
return;
}
if (!this.isPrepared) {
this.start(0);
} else if (this.avPlayer) {
this.avPlayer.play().then(() => {
console.info(TAG, 'progressTime play() current time:' + this.getCurrentTime());
this.seek(this.getCurrentTime());
this.updateIsPlay(true);
this.state = AudioPlayerState.PLAY;
})
}
}
/**
* Pause music.
*/
public pause() {
console.info(TAG, 'AVPlayer pause() isPrepared:' + this.isPrepared + ', state:' + this.state);
if (this.isPrepared && this.state === AudioPlayerState.PLAY && this.avPlayer) {
this.avPlayer.pause().then(() => {
this.state = AudioPlayerState.PAUSE;
this.updateIsPlay(false);
});
}
}
/**
* Play next music.
*
* @param isFromControl
*/
public playNextAuto(isFromControl: boolean) {
console.info(TAG, 'playNextAuto mode:' + this.playMode);
switch (this.playMode) {
case MusicPlayMode.SINGLE_CYCLE:
if (isFromControl) {
this.playNext();
} else if (this.avPlayer) {
this.avPlayer.play();
}
break;
case MusicPlayMode.ORDER:
this.playNext();
break;
case MusicPlayMode.RANDOM:
this.playRandom();
break;
default:
break;
}
}
private playNext() {
let musicIndexNum = this.musicIndex?.get()
let songListArray = this.songList?.get() as SongItem[]
console.info(TAG, 'playNext Index:' + this.musicIndex + ', length-1:' + (songListArray.length - 1));
if (musicIndexNum === songListArray.length - 1) {
this.loadAssent(0);
} else {
if (musicIndexNum != null) {
this.loadAssent(musicIndexNum + 1);
}
}
}
/**
* Play previous music.
*/
public playPrevious() {
let musicIndexNum = this.musicIndex?.get()
if (musicIndexNum != null) {
let songListArray = this.songList?.get() as SongItem[]
switch (this.playMode) {
case MusicPlayMode.RANDOM:
this.playRandom();
break;
case MusicPlayMode.ORDER:
case MusicPlayMode.SINGLE_CYCLE:
if (musicIndexNum === 0) {
this.updateMusicIndex(songListArray.length - 1);
musicIndexNum = songListArray.length - 1
} else {
this.updateMusicIndex(musicIndexNum--);
}
console.info(TAG, 'setLastIndex:' + this.musicIndex);
this.loadAssent(musicIndexNum);
break;
default:
break;
}
}
}
private playRandom() {
let musicIndexNum = this.musicIndex?.get()
let songListArray = this.songList?.get() as SongItem[]
let num = Math.round(Math.random() * (songListArray.length - 1));
if (musicIndexNum === num) {
this.playRandom();
} else {
this.updateMusicIndex(num);
this.loadAssent(num);
}
console.info(TAG, 'play Random:' + this.musicIndex);
}
/**
* Stop music
*/
public async stop() {
console.info(TAG, 'stop()');
if (this.isPrepared && this.avPlayer) {
await this.avPlayer.stop();
this.updateIsPlay(false);
this.state = AudioPlayerState.PAUSE;
}
}
public setStopTime(time: number) {
let setTime = time * 60 * 1000
if (this.stopTimer != 0) {
clearTimeout(this.stopTimer)
}
this.closeTime?.set(setTime)
if (!time) {
return
}
this.stopTimer = setInterval(() => {
this.closeTime?.set(this.closeTime.get() - 1000)
if (this.closeTime?.get() == 0) {
clearInterval(this.stopTimer)
this.pause()
}
}, 1000)
}
private async reset() {
console.info(TAG, 'reset()');
// await this.songItemBuilder.release();
if (this.avPlayer) {
await this.avPlayer.reset();
}
// this.isPrepared = false;
}
private clear() {
this.audioDetail?.set(new LessonsBriefIntroduction());
this.audioId?.set(0);
this.songItem = new SongItem();
this.songList?.set([]);
this.isPlay?.set(false);
this.currentTime?.set(0)
this.musicIndex?.set(0)
this.progress?.set(0)
this.playAll?.set(true)
this.pic?.set('')
this.isVip?.set(false)
this.sort?.set('0')
this.totalTime?.set('')
this.progressMax?.set(0)
this.closeTime?.set(0)
this.selectIndex?.set(0)
}
/**
* release avPlayer.
*/
public release() {
if (this.avPlayer && this.session && this.context) {
this.updateIsPlay(false);
this.stop();
this.reset();
this.avPlayer.release();
this.state = AudioPlayerState.IDLE;
this.clear()
// BackgroundUtil.stopContinuousTask(this.context);
this.unregisterSessionListener();
this.session.destroy((err: BusinessError) => {
if (err) {
console.error(TAG, `Failed to destroy session. Code: ${err.code}, message: ${err.message}`);
} else {
console.info(TAG, `Destroy : SUCCESS `);
}
});
}
}
public updateMusicIndex(musicIndex: number) {
console.info(TAG, 'updateMusicIndex ===>' + musicIndex);
// AppStorage.setOrCreate('selectIndex', musicIndex);
this.selectIndex?.set(musicIndex)
this.saveStatus()
if (this.musicIndex?.get() !== musicIndex) {
this.musicIndex?.set(musicIndex);
let list = this.songList?.get();
if (list && musicIndex) {
this.songItem = list[musicIndex]
}
}
console.info(TAG, 'this.session !== undefined ===>' + (this.session != undefined));
if (this.session !== undefined) {
this.setAVMetadata();
}
}
public updateMusicList(musicList: SongItem[]) {
this.songList?.set(musicList);
this.addSave()
}
private async setPlayState(playbackState: avSession.AVPlaybackState) {
if (this.session) {
this.session.setAVPlaybackState(playbackState, (err: BusinessError) => {
if (err) {
console.info(TAG, `SetAVPlaybackState BusinessError: code: ${err.code}, message: ${err.message}`);
} else {
console.info(TAG, 'SetAVPlaybackState successfully');
}
});
}
}
/**
* Update card data.
*/
public async updateCardData() {
let musicIndexNum = this.musicIndex?.get()
if (musicIndexNum != null) {
let songListArray = this.songList?.get() as SongItem[]
try {
if (!this.context) {
return;
}
PreferencesUtil.getInstance().removePreferencesFromCache(this.context);
this.formIds = await PreferencesUtil.getInstance().getFormIds(this.context);
if (this.formIds === null || this.formIds === undefined) {
console.error(TAG, 'WANG formIds is null');
return;
}
let cardSongList: Array<SongItem> = [];
if (musicIndexNum + SongConstants.ADD_INDEX_ONE === songListArray.length) {
cardSongList = songListArray.slice(SongConstants.SLICE_START_ZERO, SongConstants.SLICE_END_THREE);
} else if (musicIndexNum + SongConstants.ADD_INDEX_TWO === songListArray.length) {
cardSongList.push(songListArray[songListArray.length - 1]);
cardSongList.push(songListArray[0]);
cardSongList.push(songListArray[1]);
} else if (musicIndexNum + SongConstants.ADD_INDEX_THREE === songListArray.length) {
cardSongList = songListArray.slice(songListArray.length - SongConstants.SLICE_INDEX_TWO,
songListArray.length);
cardSongList.push(songListArray[0]);
} else {
cardSongList = songListArray.slice(musicIndexNum + SongConstants.SLICE_INDEX_ONE,
musicIndexNum + SongConstants.SLICE_INDEX_FOUR);
}
let formData: CardData = {
isPlay: this.state === AudioPlayerState.PLAY,
musicName: songListArray[musicIndexNum].title,
musicCover: songListArray[musicIndexNum].label,
musicSinger: songListArray[musicIndexNum].singer,
cardSongList: cardSongList
}
let formInfo = formBindingData.createFormBindingData(formData);
this.formIds.forEach(formId => {
formProvider.updateForm(formId, formInfo).then(() => {
console.info(TAG, 'WANG updateForm data succeed' + ', formId:' + formId);
}).catch((error: BusinessError) => {
console.error(TAG, 'updateForm err:' + JSON.stringify(error));
if (error.code === SongConstants.ID_NO_EXIT && this.context) {
PreferencesUtil.getInstance().removeFormId(this.context, formId);
}
})
})
} catch (error) {
console.error(TAG, `updateCardData err: ${(error as BusinessError).code}`);
}
}
}
/**
* Update card data on destroy.
*/
public async updateOnDestroy() {
if (this.formIds === null || this.formIds === undefined) {
console.error(TAG, 'formIds is null');
return;
}
let formData: Record<string, boolean> = {
'isPlay': false
}
let formInfo = formBindingData.createFormBindingData(formData);
for (let index = 0; index < this.formIds.length; index++) {
await formProvider.updateForm(this.formIds[index], formInfo);
}
}
playItem() {
// 设置播放参数,开始播放
let detail = this.audioDetail?.get()
let musicNum = this.musicIndex?.get() ?? 0
let playItem: avSession.AVQueueItem = {
itemId: musicNum,
description: {
// assetId: '345174646',
assetId: this.songItem.index.toString(),
// title: '我推的孩子',
title: this.songItem.title,
// artist: '啊可大咸鱼。。。',
artist: detail?.edit_user.nickname,
mediaUri: this.songItem.src,
// mediaType: 'VIDEO',
mediaType: 'AUDIO',
startPosition: this.currentTime?.get(),
albumCoverUri: detail?.pic,
albumTitle: detail?.title,
appName: 'com.ishowedu.child.peiyinos',
}
};
// 准备播放,这个不会触发真正的播放,会进行加载和缓冲
this.castController?.prepare(playItem, () => {
console.info('Preparation done');
});
// 启动播放
this.castController?.start(playItem, () => {
console.info('Playback started');
});
}
playControl() {
// 记录从avsession获取的远端控制器
// 下发播放命令
let avCommand: avSession.AVCastControlCommand = { command: 'play' };
this.castController?.sendControlCommand(avCommand);
// 下发暂停命令
avCommand = { command: 'pause' };
this.castController?.sendControlCommand(avCommand);
// 监听上下一首切换
this.castController?.on('playPrevious', () => {
console.info('PlayPrevious done');
});
this.castController?.on('playNext', () => {
console.info('PlayNext done');
});
}
}
更多关于HarmonyOS 鸿蒙Next视频投播的开发总结的实战教程也可以访问 https://www.itying.com/category-93-b0.html
更多关于HarmonyOS 鸿蒙Next视频投播的开发总结的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
HarmonyOS鸿蒙Next视频投播开发总结如下:
-
设备发现与连接:利用分布式能力,通过
DeviceManager
发现并连接目标设备,确保设备在同一网络下。 -
投播协议:支持DLNA、Miracast等协议,使用
MediaSession
和MediaController
管理媒体会话和控制。 -
媒体传输:通过
MediaPlayer
实现视频解码与播放,利用DistributedFile
进行跨设备文件传输。 -
UI交互:设计简洁的投播界面,使用
AbilitySlice
和Component
实现用户操作反馈。 -
性能优化:减少延迟,优化网络传输和媒体解码性能,确保流畅投播体验。
-
兼容性测试:在不同设备上进行测试,确保投播功能稳定可靠。